diff options
799 files changed, 47092 insertions, 14556 deletions
diff --git a/.travis.yml b/.travis.yml index 133a134758..e89774a2a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,12 +63,10 @@ matrix: - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-clang os: osx - osx_image: xcode9.3 compiler: clang - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang os: osx - osx_image: xcode9.3 compiler: clang - env: GODOT_TARGET=server TOOLS=yes CACHE_NAME=${GODOT_TARGET}-tools-gcc diff --git a/AUTHORS.md b/AUTHORS.md index d3f0592c89..e1171392d5 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -77,10 +77,12 @@ name is available. Justo Delgado (mrcdk) Kelly Thomas (KellyThomas) Kostadin Damyanov (Max-Might) + K. S. Ernest (iFire) Lee (fire) Leon Krause (eska014) m4nu3lf Marcelo Fernandez (marcelofg55) Marc Gilleron (Zylann) + Marcin Zawiejski (dragmz) Mariano Javier Suligoy (MarianoGnu) Mario Schlack (hurikhan) Martin Sjursen (binbitten) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index f4340719dc..becaa9ade9 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -185,7 +185,7 @@ Files: ./thirdparty/libpng/ Comment: libpng Copyright: 1995-1996, Guy Eric Schalnat, Group 42, Inc. 1996-1997, Andreas Dilger - 1998-2016, Glenn Randers-Pehrson + 1998-2018, Glenn Randers-Pehrson License: Zlib Files: ./thirdparty/libsimplewebm/ @@ -18,7 +18,6 @@ generous deed immortalized in the next stable release of Godot Engine. Gamblify <https://www.gamblify.com> GameDev.TV <https://www.gamedev.tv> - Skirmish <https://skirmish.io> ## Mini sponsors @@ -26,17 +25,19 @@ generous deed immortalized in the next stable release of Godot Engine. Christian Uldall Pedersen Christopher Igoe Christoph Woinke - Claudiu Dumitru E Hewert + GameDev.net Hein-Pieter van Braam - Igors Vaitkus Jamal Alyafei Jay Sistar + Loreshaper Games Matthieu Huvé Mike King Nathan Warden Neal Gompa (Conan Kudo) Pascal Julien + Patrick Aarstad + rottis Ruslan Mustakov Sébastien Manin Slobodan Milnovic @@ -61,8 +62,7 @@ generous deed immortalized in the next stable release of Godot Engine. Allen Schade Andreas Schüle Austen McRae - Bernhard Liebl - Catalin Moldovan + David Gehrig DeepSquid Fidget Sinner Florian Breisch @@ -73,21 +73,21 @@ generous deed immortalized in the next stable release of Godot Engine. Kyle Szklenski Libre-Dépanne Matthew Bennett - Olafur Gislason Paul LaMotte Ranoller + Sergey Svenne Krap Timothy Hagberg BanjoNode2D Beliar Chris Serino + Christian Padilla Conrad Curry Craig Smith Daniel Egger David Churchill Dean Harmon - Dexter Miguel John Justo Delgado Baudí KTL @@ -98,22 +98,23 @@ generous deed immortalized in the next stable release of Godot Engine. Robin Arys Rodrigo Loli Ronnie Ashlock - Rufus Xavier Sarsaparilla ScottMakesGames Thomas Bjarnelöf - William Connell Wojciech Chojnacki + Xavier PATRICELLI Zaq Poi Alessandra Pereyra Alexey Dyadchenko Amanda Haldy + Anthony Ryan Chris Brown Chris Petrich Chris Wilson Cody Parker Corey Auger D + Deadly Lampshade E.G. Eric Eric Monson @@ -125,12 +126,13 @@ generous deed immortalized in the next stable release of Godot Engine. Guilherme Felipe de C. G. da Silva Hasen Judy Heath Hayes + Jan Čejka Jay Horton Jeppe Zapp joe513 - Jordan M Lucas Juraj Móza Justin Arnold + Lars Wuethrich Leandro Voltolino Lisandro Lorea Markus Wiesner @@ -141,13 +143,14 @@ generous deed immortalized in the next stable release of Godot Engine. Pablo Cholaky Patrick Schnorbus Pete Goodwin - Phyronnaz Ruben Soares Luis + Rufus Xavier Sarsaparilla Sindre Sømme Sofox Stoned Xander Tim Dalporto Trent McPheron + Wyatt Walker ## Silver donors @@ -157,6 +160,7 @@ generous deed immortalized in the next stable release of Godot Engine. Adisibio Alder Stefano Alessandro Senese + Alexander Koppe Anders Jensen-Urstad Anthony Bongiovanni Arda Erol @@ -164,7 +168,6 @@ generous deed immortalized in the next stable release of Godot Engine. Artur Barichello Aubrey Falconer Avencherus - Bailey Bastian Böhm Benedikt Benjamin Beshara @@ -187,10 +190,11 @@ generous deed immortalized in the next stable release of Godot Engine. David David Cravens David May + Disktra Dominik Wetzel Duy Kevin Nguyen Edward Herbert - Edwin Acosta + Elias Nykrem Eric Martini Fabian Becker fengjiongmax @@ -199,6 +203,7 @@ generous deed immortalized in the next stable release of Godot Engine. Gerrit Großkopf Gerrit Procee Gilberto K. Otubo + Greg Olson Guillaume Laforte Guldoman Gumichan01 @@ -208,6 +213,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ivan Vodopiviz Jahn Johansen Jaime Ruiz-Borau Vizárraga + Jake Huxell Jed Jeff Hungerford Joel Fivat @@ -217,7 +223,9 @@ generous deed immortalized in the next stable release of Godot Engine. Jonathan Martin Jonathan Nieto Jonathon + Jon Sully Josh 'Cheeseness' Bush + Juanfran Juan Negrier Judd Julian Murgia @@ -228,9 +236,11 @@ generous deed immortalized in the next stable release of Godot Engine. Klavdij Voncina Krzysztof Jankowski Linus Lind Lundgren + Luc Magitem Luis Moraes Macil magodev + Manolis Makris Martin Eigel Martins Odabi Max R.R. Collada @@ -251,12 +261,12 @@ generous deed immortalized in the next stable release of Godot Engine. Nicolas SAN AGUSTIN Niko Leopold Noi Sek - Oleg Tyshchenko Pablo Seibelt Pan Ip + Pascal Grüter Pat LaBine Patrick Nafarrete - Patric Vormstein + Paul E Hansen Paul Mason Paweł Kowal Pierre-Igor Berthet @@ -267,21 +277,23 @@ generous deed immortalized in the next stable release of Godot Engine. Roger Burgess Roger Smith Roman Tinkov - Ryan Whited Sasori Olkof Sootstone Stefan Butucea + The K-B Theo Cranmore + Thibaud Galloy Thibault Barbaroux Thomas Bell - Thomas Hermansen Thomas Holmes Thomas Kurz + tiansheng li Tom Larrow + Tristan Crawford + Trym Nilsen Tyler Stafos UltyX Victor - Victor Gonzalez Fernandez Viktor Ferenczi waka nya werner mendizabal diff --git a/SConstruct b/SConstruct index 08c5b6d34f..2cc486fd1b 100644 --- a/SConstruct +++ b/SConstruct @@ -8,6 +8,8 @@ import os.path import glob import sys import methods +import gles_builders +from platform_methods import run_in_subprocess # scan possible build platforms @@ -409,6 +411,11 @@ if selected_platform in platform_list: env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"] env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"] + # (SH)LIBSUFFIX will be used for our own built libraries + # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default, + # so we need to append the default suffixes to keep the ability + # to link against thirdparty libraries (.a, .so, .dll, etc.). + env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]] env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"] env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] @@ -439,8 +446,8 @@ if selected_platform in platform_list: methods.no_verbose(sys, env) if (not env["platform"] == "server"): # FIXME: detect GLES3 - env.Append( BUILDERS = { 'GLES3_GLSL' : env.Builder(action = methods.build_gles3_headers, suffix = 'glsl.gen.h', src_suffix = '.glsl') } ) - env.Append( BUILDERS = { 'GLES2_GLSL' : env.Builder(action = methods.build_gles2_headers, suffix = 'glsl.gen.h', src_suffix = '.glsl') } ) + env.Append(BUILDERS = { 'GLES3_GLSL' : env.Builder(action=run_in_subprocess(gles_builders.build_gles3_headers), suffix='glsl.gen.h', src_suffix='.glsl')}) + env.Append(BUILDERS = { 'GLES2_GLSL' : env.Builder(action=run_in_subprocess(gles_builders.build_gles2_headers), suffix='glsl.gen.h', src_suffix='.glsl')}) scons_cache_path = os.environ.get("SCONS_CACHE") if scons_cache_path != None: diff --git a/core/SCsub b/core/SCsub index c508ecc37e..c8e2e10b9f 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,7 +2,9 @@ Import('env') -import methods +import core_builders +import make_binders +from platform_methods import run_in_subprocess env.core_sources = [] @@ -21,7 +23,7 @@ gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" with open("global_defaults.gen.cpp", "w") as f: - f.write(gd_cpp) + f.write(gd_cpp) # Generate AES256 script encryption key @@ -48,26 +50,27 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0" print("Invalid AES256 encryption key, not 64 bits hex: " + e) +# NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") # Add required thirdparty code. Header paths are hardcoded, we don't need to append # to the include path (saves a few chars on the compiler invocation for touchy MSVC...) thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ - # C sources - "base64.c", - "fastlz.c", - "sha256.c", - "smaz.c", - - # C++ sources - "aes256.cpp", - "hq2x.cpp", - "md5.cpp", - "pcg.cpp", - "triangulator.cpp", + # C sources + "base64.c", + "fastlz.c", + "sha256.c", + "smaz.c", + + # C++ sources + "aes256.cpp", + "hq2x.cpp", + "md5.cpp", + "pcg.cpp", + "triangulator.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env.add_source_files(env.core_sources, thirdparty_sources) @@ -76,9 +79,9 @@ env.add_source_files(env.core_sources, thirdparty_sources) # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" thirdparty_minizip_sources = [ - "ioapi.c", - "unzip.c", - "zip.c", + "ioapi.c", + "unzip.c", + "zip.c", ] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] env.add_source_files(env.core_sources, thirdparty_minizip_sources) @@ -92,20 +95,19 @@ env.add_source_files(env.core_sources, "*.cpp") # Make binders -import make_binders -env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run) +env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) # Authors env.Depends('#core/authors.gen.h', "../AUTHORS.md") -env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", methods.make_authors_header) +env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header)) # Donors env.Depends('#core/donors.gen.h', "../DONORS.md") -env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", methods.make_donors_header) +env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", run_in_subprocess(core_builders.make_donors_header)) # License env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]) -env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], methods.make_license_header) +env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header)) # Chain load SCsubs SConscript('os/SCsub') diff --git a/core/array.cpp b/core/array.cpp index 9e3250fd47..44c553e4eb 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -72,7 +72,7 @@ void Array::_unref() const { Variant &Array::operator[](int p_idx) { - return _p->array[p_idx]; + return _p->array.write[p_idx]; } const Variant &Array::operator[](int p_idx) const { @@ -259,7 +259,7 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { ERR_FAIL_NULL_V(p_obj, *this); - SortArray<Variant, _ArrayVariantSortCustom> avs; + SortArray<Variant, _ArrayVariantSortCustom, true> avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 7a14e85f20..626d41dca0 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -112,11 +112,22 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) { return ret; }; +#ifndef DISABLE_DEPRECATED bool _ResourceLoader::has(const String &p_path) { + WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists()."); + return has_cached(p_path); +} +#endif // DISABLE_DEPRECATED + +bool _ResourceLoader::has_cached(const String &p_path) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); return ResourceCache::has(local_path); -}; +} + +bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + return ResourceLoader::exists(p_path, p_type_hint); +} void _ResourceLoader::_bind_methods() { @@ -125,7 +136,11 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); + ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); + ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has); +#endif // DISABLE_DEPRECATED } _ResourceLoader::_ResourceLoader() { @@ -221,6 +236,10 @@ String _OS::get_audio_driver_name(int p_driver) const { return OS::get_singleton()->get_audio_driver_name(p_driver); } +PoolStringArray _OS::get_connected_midi_inputs() { + return OS::get_singleton()->get_connected_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -1058,6 +1077,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 48b7b74005..311372aeca 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -55,7 +55,11 @@ public: PoolVector<String> get_recognized_extensions_for_type(const String &p_type); void set_abort_on_missing_resources(bool p_abort); PoolStringArray get_dependencies(const String &p_path); +#ifndef DISABLE_DEPRECATED bool has(const String &p_path); +#endif // DISABLE_DEPRECATED + bool has_cached(const String &p_path); + bool exists(const String &p_path, const String &p_type_hint = ""); _ResourceLoader(); }; @@ -152,6 +156,8 @@ public: virtual int get_audio_driver_count() const; virtual String get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen); diff --git a/core/class_db.cpp b/core/class_db.cpp index f97eaf6099..03b214aa41 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -58,8 +58,8 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(2); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); return md; } @@ -68,9 +68,9 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(3); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); return md; } @@ -79,10 +79,10 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(4); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); return md; } @@ -91,11 +91,11 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(5); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); return md; } @@ -104,12 +104,12 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(6); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); return md; } @@ -118,13 +118,13 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(7); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); return md; } @@ -133,14 +133,14 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(8); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); return md; } @@ -149,15 +149,15 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(9); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); return md; } @@ -166,16 +166,16 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(10); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); return md; } @@ -184,17 +184,17 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(11); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); return md; } @@ -203,18 +203,18 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(12); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); return md; } @@ -223,19 +223,19 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(13); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); - md.args[12] = StaticCString::create(p_arg13); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); + md.args.write[12] = StaticCString::create(p_arg13); return md; } @@ -1246,7 +1246,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c defvals.resize(p_defcount); for (int i = 0; i < p_defcount; i++) { - defvals[i] = *p_defs[p_defcount - i - 1]; + defvals.write[i] = *p_defs[p_defcount - i - 1]; } p_bind->set_default_arguments(defvals); diff --git a/core/color.cpp b/core/color.cpp index 88e57ec6e2..fcfcf20355 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -49,6 +49,7 @@ uint32_t Color::to_argb32() const { } uint32_t Color::to_abgr32() const { + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; c |= (uint8_t)Math::round(b * 255); @@ -73,6 +74,45 @@ uint32_t Color::to_rgba32() const { return c; } +uint64_t Color::to_abgr64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + + return c; +} + +uint64_t Color::to_argb64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + + return c; +} + +uint64_t Color::to_rgba64() const { + + uint64_t c = (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(a * 65535); + + return c; +} + float Color::get_h() const { float min = MIN(r, g); @@ -200,6 +240,19 @@ Color Color::hex(uint32_t p_hex) { return Color(r, g, b, a); } +Color Color::hex64(uint64_t p_hex) { + + float a = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float b = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float g = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float r = (p_hex & 0xFFFF) / 65535.0; + + return Color(r, g, b, a); +} + static float _parse_col(const String &p_str, int p_ofs) { int ig = 0; diff --git a/core/color.h b/core/color.h index a2015a34d6..c0516e55fe 100644 --- a/core/color.h +++ b/core/color.h @@ -55,6 +55,9 @@ struct Color { uint32_t to_rgba32() const; uint32_t to_argb32() const; uint32_t to_abgr32() const; + uint64_t to_rgba64() const; + uint64_t to_argb64() const; + uint64_t to_abgr64() const; float gray() const; float get_h() const; float get_s() const; @@ -186,6 +189,7 @@ struct Color { } static Color hex(uint32_t p_hex); + static Color hex64(uint64_t p_hex); static Color html(const String &p_color); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 266d793af7..5c1fd26e2a 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -73,7 +73,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { Pair<int, CharString> p; p.first = idx; p.second = cs; - buckets[h % size].push_back(p); + buckets.write[h % size].push_back(p); //compress string CharString src_s = p_from->get_message(E->get()).operator String().utf8(); @@ -100,7 +100,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { ps.compressed[0] = 0; } - compressed[idx] = ps; + compressed.write[idx] = ps; total_compression_size += ps.compressed.size(); total_string_size += src_s.size(); idx++; @@ -111,8 +111,8 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Vector<Pair<int, CharString> > &b = buckets[i]; - Map<uint32_t, int> &t = table[i]; + const Vector<Pair<int, CharString> > &b = buckets[i]; + Map<uint32_t, int> &t = table.write[i]; if (b.size() == 0) continue; @@ -136,7 +136,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } } - hfunc_table[i] = d; + hfunc_table.write[i] = d; bucket_table_size += 2 + b.size() * 4; } @@ -157,7 +157,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Map<uint32_t, int> &t = table[i]; + const Map<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; diff --git a/core/core_builders.py b/core/core_builders.py new file mode 100644 index 0000000000..90e505aab9 --- /dev/null +++ b/core/core_builders.py @@ -0,0 +1,236 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main +from compat import iteritems, itervalues, open_utf8, escape_string + + +def make_authors_header(target, source, env): + sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] + sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_AUTHORS_H\n") + g.write("#define _EDITOR_AUTHORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_donors_header(target, source, env): + sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", + "Gold donors", "Silver donors", "Bronze donors"] + sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI", + "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_DONORS_H\n") + g.write("#define _EDITOR_DONORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_license_header(target, source, env): + src_copyright = source[0] + src_license = source[1] + dst = target[0] + + class LicenseReader: + def __init__(self, license_file): + self._license_file = license_file + self.line_num = 0 + self.current = self.next_line() + + def next_line(self): + line = self._license_file.readline() + self.line_num += 1 + while line.startswith("#"): + line = self._license_file.readline() + self.line_num += 1 + self.current = line + return line + + def next_tag(self): + if not ':' in self.current: + return ('', []) + tag, line = self.current.split(":", 1) + lines = [line.strip()] + while self.next_line() and self.current.startswith(" "): + lines.append(self.current.strip()) + return (tag, lines) + + from collections import OrderedDict + projects = OrderedDict() + license_list = [] + + with open_utf8(src_copyright, "r") as copyright_file: + reader = LicenseReader(copyright_file) + part = {} + while reader.current: + tag, content = reader.next_tag() + if tag in ("Files", "Copyright", "License"): + part[tag] = content[:] + elif tag == "Comment": + # attach part to named project + projects[content[0]] = projects.get(content[0], []) + [part] + + if not tag or not reader.current: + # end of a paragraph start a new part + if "License" in part and not "Files" in part: + # no Files tag in this one, so assume standalone license + license_list.append(part["License"]) + part = {} + reader.next_line() + + data_list = [] + for project in itervalues(projects): + for part in project: + part["file_index"] = len(data_list) + data_list += part["Files"] + part["copyright_index"] = len(data_list) + data_list += part["Copyright"] + + with open_utf8(dst, "w") as f: + + f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + f.write("#ifndef _EDITOR_LICENSE_H\n") + f.write("#define _EDITOR_LICENSE_H\n") + f.write("const char *const GODOT_LICENSE_TEXT =") + + with open_utf8(src_license, "r") as license_file: + for line in license_file: + escaped_string = escape_string(line.strip()) + f.write("\n\t\t\"" + escaped_string + "\\n\"") + f.write(";\n\n") + + f.write("struct ComponentCopyrightPart {\n" + "\tconst char *license;\n" + "\tconst char *const *files;\n" + "\tconst char *const *copyright_statements;\n" + "\tint file_count;\n" + "\tint copyright_count;\n" + "};\n\n") + + f.write("struct ComponentCopyright {\n" + "\tconst char *name;\n" + "\tconst ComponentCopyrightPart *parts;\n" + "\tint part_count;\n" + "};\n\n") + + f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") + for line in data_list: + f.write("\t\"" + escape_string(line) + "\",\n") + f.write("};\n\n") + + f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") + part_index = 0 + part_indexes = {} + for project_name, project in iteritems(projects): + part_indexes[project_name] = part_index + for part in project: + f.write("\t{ \"" + escape_string(part["License"][0]) + "\", " + + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " + + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " + + str(len(part["Files"])) + ", " + + str(len(part["Copyright"])) + " },\n") + part_index += 1 + f.write("};\n\n") + + f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") + + f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") + for project_name, project in iteritems(projects): + f.write("\t{ \"" + escape_string(project_name) + "\", " + + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " + + str(len(project)) + " },\n") + f.write("};\n\n") + + f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") + + f.write("const char *const LICENSE_NAMES[] = {\n") + for l in license_list: + f.write("\t\"" + escape_string(l[0]) + "\",\n") + f.write("};\n\n") + + f.write("const char *const LICENSE_BODIES[] = {\n\n") + for l in license_list: + for line in l[1:]: + if line == ".": + f.write("\t\"\\n\"\n") + else: + f.write("\t\"" + escape_string(line) + "\\n\"\n") + f.write("\t\"\",\n\n") + f.write("};\n\n") + + f.write("#endif\n") + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/core/cowdata.h b/core/cowdata.h new file mode 100644 index 0000000000..6a8f644d53 --- /dev/null +++ b/core/cowdata.h @@ -0,0 +1,338 @@ +/*************************************************************************/ +/* cowdata.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COWDATA_H_ +#define COWDATA_H_ + +#include "os/memory.h" +#include "safe_refcount.h" + +template <class T> +class Vector; +class String; +class CharString; +template <class T, class V> +class VMap; + +template <class T> +class CowData { + template <class TV> + friend class Vector; + friend class String; + friend class CharString; + template <class TV, class VV> + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ uint32_t *_get_refcount() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 1; + } + + _FORCE_INLINE_ T *_get_data() const { + + if (!_ptr) + return NULL; + return reinterpret_cast<T *>(_ptr); + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) return false; + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + void _copy_on_write(); + +public: + void operator=(const CowData<T> &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return (T *)_get_data(); + } + + _FORCE_INLINE_ const T *ptr() const { + return _get_data(); + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) + return *size; + else + return 0; + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == 0; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _get_data()[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _get_data()[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + + CRASH_BAD_INDEX(p_index, size()); + + return _get_data()[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + + p[i] = p[i + 1]; + }; + + resize(len - 1); + }; + + Error insert(int p_pos, const T &p_val) { + + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) + set(i, get(i - 1)); + set(p_pos, p_val); + + return OK; + }; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; +}; + +template <class T> +void CowData<T>::_unref(void *p_data) { + + if (!p_data) + return; + + uint32_t *refc = _get_refcount(); + + if (atomic_decrement(refc) > 0) + return; // still in use + // clean up + + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template <class T> +void CowData<T>::_copy_on_write() { + + if (!_ptr) + return; + + uint32_t *refc = _get_refcount(); + + if (unlikely(*refc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + *(mem_new - 2) = 1; //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + for (uint32_t i = 0; i < current_size; i++) { + + memnew_placement(&_data[i], T(_get_data()[i])); + } + + _unref(_ptr); + _ptr = _data; + } +} + +template <class T> +Error CowData<T>::resize(int p_size) { + + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + if (p_size == size()) + return OK; + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = NULL; + return OK; + } + + // possibly changing size, copy on write + _copy_on_write(); + + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > size()) { + + if (size() == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + *(ptr - 2) = 1; //refcount + + _ptr = (T *)ptr; + + } else { + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + _ptr = (T *)(_ptrnew); + } + + // construct the newly created elements + T *elems = _get_data(); + + for (int i = *_get_size(); i < p_size; i++) { + + memnew_placement(&elems[i], T); + } + + *_get_size() = p_size; + + } else if (p_size < size()) { + + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + + T *t = &_get_data()[i]; + t->~T(); + } + + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + + _ptr = (T *)(_ptrnew); + + *_get_size() = p_size; + } + + return OK; +} + +template <class T> +void CowData<T>::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template <class T> +void CowData<T>::_ref(const CowData &p_from) { + + if (_ptr == p_from._ptr) + return; // self assign, do nothing. + + _unref(_ptr); + _ptr = NULL; + + if (!p_from._ptr) + return; //nothing to do + + if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template <class T> +CowData<T>::CowData() { + + _ptr = NULL; +} + +template <class T> +CowData<T>::~CowData() { + + _unref(_ptr); +} + +#endif /* COW_H_ */ diff --git a/core/dictionary.cpp b/core/dictionary.cpp index d68411a572..42d9eab310 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -140,6 +140,11 @@ void Dictionary::erase(const Variant &p_key) { _p->variant_map.erase(p_key); } +bool Dictionary::erase_checked(const Variant &p_key) { + + return _p->variant_map.erase(p_key); +} + bool Dictionary::operator==(const Dictionary &p_dictionary) const { return _p == p_dictionary._p; diff --git a/core/dictionary.h b/core/dictionary.h index 84a5cafe1d..00ec67fb99 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -66,6 +66,7 @@ public: bool has_all(const Array &p_keys) const; void erase(const Variant &p_key); + bool erase_checked(const Variant &p_key); bool operator==(const Dictionary &p_dictionary) const; diff --git a/core/error_macros.h b/core/error_macros.h index 3587e01d54..bee738ceea 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -313,7 +313,7 @@ extern bool _err_error_exists; #define WARN_DEPRECATED \ { \ - static bool warning_shown = false; \ + static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ _err_error_exists = false; \ diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 5b4dd05dbf..187813f9d0 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList); VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(JoystickList); +VARIANT_ENUM_CAST(MidiMessageList); void register_global_constants() { @@ -458,6 +459,15 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + // midi + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + // error list BIND_GLOBAL_ENUM_CONSTANT(OK); diff --git a/core/image.cpp b/core/image.cpp index b9386a1b6d..65905c83e8 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -33,6 +33,7 @@ #include "core/io/image_loader.h" #include "core/os/copymem.h" #include "hash_map.h" +#include "math_funcs.h" #include "print_string.h" #include "thirdparty/misc/hq2x.h" @@ -525,7 +526,7 @@ static double _bicubic_interp_kernel(double x) { } template <int CC> -static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size int width = p_src_width; @@ -555,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi // initial pixel value - uint8_t *dst = p_dst + (y * p_dst_width + x) * CC; + uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC; double color[CC]; for (int i = 0; i < CC; i++) { @@ -583,7 +584,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi ox2 = xmax; // get pixel of original image - const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC; + const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC; for (int i = 0; i < CC; i++) { @@ -600,7 +601,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi } template <int CC> -static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { FRAC_BITS = 8, @@ -655,7 +656,7 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src } template <int CC> -static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { @@ -676,6 +677,16 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_ } } +static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { + + uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); + + for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { + + p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8; + } +} + void Image::resize_to_po2(bool p_square) { if (!_can_modify(format)) { @@ -707,6 +718,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL(); } + bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; + ERR_FAIL_COND(p_width <= 0); ERR_FAIL_COND(p_height <= 0); ERR_FAIL_COND(p_width > MAX_WIDTH); @@ -717,6 +730,32 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { Image dst(p_width, p_height, 0, format); + // Setup mipmap-aware scaling + Image dst2; + int mip1; + int mip2; + float mip1_weight; + if (mipmap_aware) { + float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f; + if (avg_scale >= 1.0f) { + mipmap_aware = false; + } else { + float level = Math::log(1.0f / avg_scale) / Math::log(2.0f); + mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count()); + mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count()); + mip1_weight = 1.0f - (level - mip1); + } + } + bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; + if (interpolate_mipmaps) { + dst2.create(p_width, p_height, 0, format); + } + bool had_mipmaps = mipmaps; + if (interpolate_mipmaps && !had_mipmaps) { + generate_mipmaps(); + } + // -- + PoolVector<uint8_t>::Read r = data.read(); const unsigned char *r_ptr = r.ptr(); @@ -734,13 +773,57 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; } } break; - case INTERPOLATE_BILINEAR: { + case INTERPOLATE_BILINEAR: + case INTERPOLATE_TRILINEAR: { + + for (int i = 0; i < 2; ++i) { + int src_width; + int src_height; + const unsigned char *src_ptr; + + if (!mipmap_aware) { + if (i == 0) { + // Standard behavior + src_width = width; + src_height = height; + src_ptr = r_ptr; + } else { + // No need for a second iteration + break; + } + } else { + if (i == 0) { + // Read from the first mipmap that will be interpolated + // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level) + int offs; + _get_mipmap_offset_and_size(mip1, offs, src_width, src_height); + src_ptr = r_ptr + offs; + } else if (!interpolate_mipmaps) { + // No need generate a second image + break; + } else { + // Switch to read from the second mipmap that will be interpolated + int offs; + _get_mipmap_offset_and_size(mip2, offs, src_width, src_height); + src_ptr = r_ptr + offs; + // Switch to write to the second destination image + w = dst2.data.write(); + w_ptr = w.ptr(); + } + } - switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + switch (get_format_pixel_size(format)) { + case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } + + if (interpolate_mipmaps) { + // Switch to read again from the first scaled mipmap to overlay it over the second + r = dst.data.read(); + _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format)); } } break; @@ -759,7 +842,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { r = PoolVector<uint8_t>::Read(); w = PoolVector<uint8_t>::Write(); - if (mipmaps > 0) + if (interpolate_mipmaps) { + dst._copy_internals_from(dst2); + } + + if (had_mipmaps) dst.generate_mipmaps(); _copy_internals_from(dst); @@ -900,8 +987,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -1217,7 +1306,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1505,10 +1594,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -2289,6 +2378,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); @@ -2404,6 +2504,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); + BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); diff --git a/core/image.h b/core/image.h index 3792103a87..c8dd647c31 100644 --- a/core/image.h +++ b/core/image.h @@ -108,13 +108,16 @@ public: INTERPOLATE_NEAREST, INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, + INTERPOLATE_TRILINEAR, + /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else @@ -270,7 +273,7 @@ public: static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); enum CompressMode { @@ -327,6 +330,7 @@ public: }; DetectChannels get_detected_channels(); + void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 221f457b78..bb7a444ccc 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -89,7 +89,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 for (size_t i = 0; i < ds; i += 16) { - aes256_decrypt_ecb(&ctx, &data[i]); + aes256_decrypt_ecb(&ctx, &data.write[i]); } aes256_done(&ctx); @@ -117,7 +117,7 @@ Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const Str key.resize(32); for (int i = 0; i < 32; i++) { - key[i] = cs[i]; + key.write[i] = cs[i]; } return open_and_parse(p_base, key, p_mode); @@ -148,7 +148,7 @@ void FileAccessEncrypted::close() { compressed.resize(len); zeromem(compressed.ptrw(), len); for (int i = 0; i < data.size(); i++) { - compressed[i] = data[i]; + compressed.write[i] = data[i]; } aes256_context ctx; @@ -156,7 +156,7 @@ void FileAccessEncrypted::close() { for (size_t i = 0; i < len; i += 16) { - aes256_encrypt_ecb(&ctx, &compressed[i]); + aes256_encrypt_ecb(&ctx, &compressed.write[i]); } aes256_done(&ctx); @@ -263,7 +263,7 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { data.resize(pos + p_length); for (int i = 0; i < p_length; i++) { - data[pos + i] = p_src[i]; + data.write[pos + i] = p_src[i]; } pos += p_length; } @@ -280,7 +280,7 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { ERR_FAIL_COND(!writing); if (pos < data.size()) { - data[pos] = p_dest; + data.write[pos] = p_dest; pos++; } else if (pos == data.size()) { data.push_back(p_dest); diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 1aa1e4a595..c4eb2848b1 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -92,7 +92,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { Map<String, Vector<uint8_t> >::Element *E = files->find(name); ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); - data = &(E->get()[0]); + data = E->get().ptrw(); length = E->get().size(); pos = 0; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 21e3a4172b..e0a2dbf507 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -258,8 +258,8 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) } buffer_mutex->lock(); - pages[page].buffer = p_block; - pages[page].queued = false; + pages.write[page].buffer = p_block; + pages.write[page].queued = false; buffer_mutex->unlock(); if (waiting_on_page == page) { @@ -389,7 +389,7 @@ void FileAccessNetwork::_queue_page(int p_page) const { br.offset = size_t(p_page) * page_size; br.size = page_size; nc->block_requests.push_back(br); - pages[p_page].queued = true; + pages.write[p_page].queued = true; nc->blockrequest_mutex->unlock(); DEBUG_PRINT("QUEUE PAGE POST"); nc->sem->post(); @@ -433,12 +433,12 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { _queue_page(page + j); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); //queue pages buffer_mutex->unlock(); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); last_page_buff = buff; last_page = page; } diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index f1620f1493..2425bb6d69 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -552,7 +552,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { int rec = 0; - err = _get_http_data(&chunk[chunk.size() - chunk_left], chunk_left, rec); + err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { break; } diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 8ebd9d6cd9..b8fd13d67c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -107,3 +107,83 @@ void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { ERR_FAIL_COND(loader_count >= MAX_LOADERS); loader[loader_count++] = p_loader; } + +///////////////// + +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + uint8_t header[4] = { 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; + if (unrecognized) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + String extension = f->get_pascal_string(); + + int idx = -1; + + for (int i = 0; i < ImageLoader::loader_count; i++) { + if (ImageLoader::loader[i]->recognize(extension)) { + idx = i; + break; + } + } + + if (idx == -1) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + Ref<Image> image; + image.instance(); + + Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); + + memdelete(f); + + if (err != OK) { + if (r_error) { + *r_error = err; + } + return RES(); + } + + if (r_error) { + *r_error = OK; + } + + return image; +} + +void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("image"); +} + +bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { + + return p_type == "Image"; +} + +String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); +} diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 052a8b8a40..fbb654c326 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -32,9 +32,11 @@ #define IMAGE_LOADER_H #include "image.h" +#include "io/resource_loader.h" #include "list.h" #include "os/file_access.h" #include "ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -55,6 +57,7 @@ class ImageLoader; class ImageFormatLoader { friend class ImageLoader; + friend class ResourceFormatLoaderImage; protected: virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; @@ -70,7 +73,7 @@ class ImageLoader { enum { MAX_LOADERS = 8 }; - + friend class ResourceFormatLoaderImage; static ImageFormatLoader *loader[MAX_LOADERS]; static int loader_count; @@ -83,4 +86,12 @@ public: static void add_image_format_loader(ImageFormatLoader *p_loader); }; +class ResourceFormatLoaderImage : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 0a3a6c1ba1..e97df0c261 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -32,8 +32,13 @@ #include "os/keyboard.h" #include "print_string.h" #include "reference.h" +#include <limits.h> #include <stdio.h> +#define _S(a) ((int32_t)a) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) + void EncodedObjectAsID::_bind_methods() { ClassDB::bind_method(D_METHOD("set_object_id", "id"), &EncodedObjectAsID::set_object_id); ClassDB::bind_method(D_METHOD("get_object_id"), &EncodedObjectAsID::get_object_id); @@ -60,23 +65,31 @@ EncodedObjectAsID::EncodedObjectAsID() { static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); + int32_t pad = 0; + + // Handle padding + if (strlen % 4) { + pad = 4 - strlen % 4; + } + buf += 4; len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_FILE_EOF); + + // Ensure buffer is big enough + ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF); + ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF); String str; - str.parse_utf8((const char *)buf, strlen); + ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen), ERR_INVALID_DATA); r_string = str; - //handle padding - if (strlen % 4) { - strlen += 4 - strlen % 4; - } + // Add padding + strlen += pad; + // Update buffer pos, left data count, and return size buf += strlen; len -= strlen; - if (r_len) { (*r_len) += 4 + strlen; } @@ -119,14 +132,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::INT: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; if (r_len) @@ -136,14 +150,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::REAL: { - ERR_FAIL_COND_V(len < (int)4, ERR_INVALID_DATA); - if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; if (r_len) @@ -164,7 +178,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < (int)4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -176,7 +190,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; // 5 case Variant::RECT2: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -190,7 +204,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR3: { - ERR_FAIL_COND_V(len < (int)4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -203,7 +217,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM2D: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { @@ -220,7 +234,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); val.normal.y = decode_float(&buf[4]); @@ -234,7 +248,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -248,7 +262,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -264,7 +278,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < (int)4 * 9, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -281,7 +295,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < (int)4 * 12, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -303,7 +317,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); val.g = decode_float(&buf[4]); @@ -318,7 +332,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::NODE_PATH: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); if (strlen & 0x80000000) { //new format @@ -343,31 +357,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int for (uint32_t i = 0; i < total; i++) { - ERR_FAIL_COND_V((int)len < 4, ERR_INVALID_DATA); - strlen = decode_uint32(buf); - - int pad = 0; - - if (strlen % 4) - pad += 4 - strlen % 4; - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen + pad > len, ERR_INVALID_DATA); - String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; if (i < namecount) names.push_back(str); else subnames.push_back(str); - - buf += strlen + pad; - len -= strlen + pad; - - if (r_len) - (*r_len) += 4 + strlen + pad; } r_variant = NodePath(names, subnames, flags & 1); @@ -375,17 +373,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } else { //old format, just a string - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - String str; - str.parse_utf8((const char *)buf, strlen); - - r_variant = NodePath(str); - - if (r_len) - (*r_len) += 4 + strlen; + ERR_FAIL_V(ERR_INVALID_DATA); } } break; @@ -402,6 +390,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = decode_uint64(buf); if (r_len) (*r_len) += 8; @@ -475,7 +464,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -488,7 +477,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { Variant key, value; @@ -520,7 +509,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -533,7 +522,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { int used = 0; Variant v; @@ -555,17 +544,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_BYTE_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count > len, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count > len, ERR_INVALID_DATA); PoolVector<uint8_t> data; if (count) { data.resize(count); PoolVector<uint8_t>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = buf[i]; } @@ -585,10 +574,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_INT_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<int> data; @@ -596,7 +586,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const int*rbuf=(const int*)buf; data.resize(count); PoolVector<int>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_uint32(&buf[i * 4]); } @@ -612,10 +602,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_REAL_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<float> data; @@ -623,7 +614,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const float*rbuf=(const float*)buf; data.resize(count); PoolVector<float>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_float(&buf[i * 4]); } @@ -640,7 +631,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_STRING_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); PoolVector<String> strings; buf += 4; @@ -650,35 +641,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += 4; //printf("string count: %i\n",count); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - //printf("loaded string: %s\n",(const char*)buf); String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; strings.push_back(str); - - buf += strlen; - len -= strlen; - - if (r_len) - (*r_len) += 4 + strlen; - - if (strlen % 4) { - int pad = 4 - (strlen % 4); - buf += pad; - len -= pad; - if (r_len) { - (*r_len) += pad; - } - } } r_variant = strings; @@ -687,11 +657,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR2_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 2 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA); PoolVector<Vector2> varray; if (r_len) { @@ -702,7 +673,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector2>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); @@ -722,11 +693,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR3_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 3 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA); + PoolVector<Vector3> varray; if (r_len) { @@ -737,7 +710,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector3>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); @@ -758,11 +731,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_COLOR_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 4 > len, ERR_INVALID_DATA); + PoolVector<Color> carray; if (r_len) { @@ -773,7 +748,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int carray.resize(count); PoolVector<Color>::Write w = carray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); @@ -1323,7 +1298,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo while (r_len % 4) { r_len++; //pad if (buf) - buf++; + *(buf++) = 0; } } diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index ffd3ecaed0..8e67f1c97a 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -282,10 +282,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ ERR_FAIL_COND(p_offset >= p_packet_len); int vlen; - Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); ERR_FAIL_COND(err != OK); //args[i]=p_packet[3+i]; - argp[i] = &args[i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -354,8 +354,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, Vector<uint8_t> packet; packet.resize(1 + len); - packet[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet[1]); + packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; + encode_cstring(pname.get_data(), &packet.write[1]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -415,9 +415,9 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int Vector<uint8_t> packet; packet.resize(1 + 4 + len); - packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet[1]); - encode_cstring(pname.get_data(), &packet[5]); + packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; + encode_uint32(psc->id, &packet.write[1]); + encode_cstring(pname.get_data(), &packet.write[5]); network_peer->set_target_peer(E->get()); //to all of you network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); @@ -482,19 +482,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p //encode type MAKE_ROOM(1); - packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; //encode ID MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache[ofs])); + encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; //encode function name CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache[ofs])); + encode_cstring(name.get_data(), &(packet_cache.write[ofs])); ofs += len; if (p_set) { @@ -502,19 +502,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p Error err = encode_variant(*p_arg[0], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache[ofs]), len); + encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { //call arguments MAKE_ROOM(ofs + 1); - packet_cache[ofs] = p_argcount; + packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache[ofs]), len); + encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } @@ -537,7 +537,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache[ofs])); + encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { @@ -554,11 +554,11 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (F->get() == true) { //this one confirmed path, so use id - encode_uint32(psc->id, &(packet_cache[1])); + encode_uint32(psc->id, &(packet_cache.write[1])); network_peer->put_packet(packet_cache.ptr(), ofs); } else { //this one did not confirm path yet, so use entire path (sorry!) - encode_uint32(0x80000000 | ofs, &(packet_cache[1])); //offset to path and flag + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); //offset to path and flag network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -712,8 +712,8 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked MAKE_ROOM(p_data.size() + 1); PoolVector<uint8_t>::Read r = p_data.read(); - packet_cache[0] = NETWORK_COMMAND_RAW; - memcpy(&packet_cache[1], &r[0], p_data.size()); + packet_cache.write[0] = NETWORK_COMMAND_RAW; + memcpy(&packet_cache.write[1], &r[0], p_data.size()); network_peer->set_target_peer(p_to); network_peer->set_transfer_mode(p_mode); @@ -773,7 +773,7 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const { void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index b777a9f960..dc4997dfc2 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -173,7 +173,7 @@ Error PacketPeerStream::_poll_buffer() const { int read = 0; ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); - Error err = peer->get_partial_data(&input_buffer[0], ring_buffer.space_left(), read); + Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); if (err) return err; if (read == 0) @@ -226,7 +226,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE); ring_buffer.read(lbuf, 4); //get rid of first 4 bytes - ring_buffer.read(&input_buffer[0], len); // read packet + ring_buffer.read(input_buffer.ptrw(), len); // read packet *r_buffer = &input_buffer[0]; r_buffer_size = len; @@ -247,8 +247,8 @@ Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); - encode_uint32(p_buffer_size, &output_buffer[0]); - uint8_t *dst = &output_buffer[4]; + encode_uint32(p_buffer_size, output_buffer.ptrw()); + uint8_t *dst = &output_buffer.write[4]; for (int i = 0; i < p_buffer_size; i++) dst[i] = p_buffer[i]; diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index b6377662de..2fd73db27d 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -120,7 +120,7 @@ Error PCKPacker::flush(bool p_verbose) { for (int i = 0; i < files.size(); i++) { file->store_pascal_string(files[i].path); - files[i].offset_offset = file->get_position(); + files.write[i].offset_offset = file->get_position(); file->store_64(0); // offset file->store_64(files[i].size); // size diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0c626c197b..02c2c6ce1a 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -894,7 +894,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { for (uint32_t i = 0; i < string_table_size; i++) { StringName s = get_unicode_string(); - string_map[i] = s; + string_map.write[i] = s; } print_bl("strings: " + itos(string_table_size)); @@ -1834,7 +1834,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_order.resize(external_resources.size()); for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - save_order[E->get()] = E->key(); + save_order.write[E->get()] = E->key(); } for (int i = 0; i < save_order.size(); i++) { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1351030d1e..8b0655deb0 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -123,6 +123,10 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoader::load_interactive(const Stri return ril; } +bool ResourceFormatLoader::exists(const String &p_path) const { + return FileAccess::exists(p_path); //by default just check file +} + RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error) { String path = p_path; @@ -239,6 +243,36 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p return res; } +bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + + String local_path; + if (p_path.is_rel_path()) + local_path = "res://" + p_path; + else + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + + if (ResourceCache::has(local_path)) { + + return true; // If cached, it probably exists + } + + bool xl_remapped = false; + String path = _path_remap(local_path, &xl_remapped); + + // Try all loaders and pick the first match for the type hint + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(path, p_type_hint)) { + continue; + } + + if (loader[i]->exists(path)) + return true; + } + + return false; +} + Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { if (r_error) @@ -556,7 +590,7 @@ void ResourceLoader::load_translation_remaps() { Vector<String> lang_remaps; lang_remaps.resize(langs.size()); for (int i = 0; i < langs.size(); i++) { - lang_remaps[i] = langs[i]; + lang_remaps.write[i] = langs[i]; } translation_remaps[String(E->get())] = lang_remaps; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 9be82abb42..f78464ef0c 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -60,6 +60,7 @@ class ResourceFormatLoader { public: virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; @@ -106,6 +107,7 @@ class ResourceLoader { public: static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); + static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions); static void add_resource_format_loader(ResourceFormatLoader *p_format_loader, bool p_at_front = false); diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 927b9f6366..3e0ee088c2 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -331,7 +331,7 @@ String StreamPeer::get_string(int p_bytes) { ERR_FAIL_COND_V(err != OK, String()); err = get_data((uint8_t *)&buf[0], p_bytes); ERR_FAIL_COND_V(err != OK, String()); - buf[p_bytes] = 0; + buf.write[p_bytes] = 0; return buf.ptr(); } String StreamPeer::get_utf8_string(int p_bytes) { diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index c71af6b641..e7e9662d24 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -92,7 +92,7 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 16d5e3c282..85c1fc5ddf 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -54,32 +54,25 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S int line = 1; bool skip_this = false; bool skip_next = false; + bool is_eof = false; - while (true) { + while (!is_eof) { - String l = f->get_line(); + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); - if (f->eof_reached()) { + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.empty()) { - if (status == STATUS_READING_STRING) { - - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") - config = msg_str; - break; - - } else if (status == STATUS_NONE) + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } else { break; - - memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + } } - l = l.strip_edges(); - if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { @@ -160,6 +153,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S f->close(); memdelete(f); + if (status == STATUS_READING_STRING) { + + if (msg_id != "") { + if (!skip_this) + translation->add_message(msg_id, msg_str); + } else if (config == "") + config = msg_str; + } + if (config == "") { ERR_EXPLAIN("No config found in file: " + p_path); ERR_FAIL_V(RES()); diff --git a/core/make_binders.py b/core/make_binders.py index 6a7602cd5d..4c61b90d99 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -1,6 +1,5 @@ # -*- coding: ibm850 -*- - template_typed = """ #ifdef TYPED_METHOD_BIND template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> @@ -265,8 +264,13 @@ def run(target, source, env): else: text += t - with open(target[0].path, "w") as f: + with open(target[0], "w") as f: f.write(text) - with open(target[1].path, "w") as f: + with open(target[1], "w") as f: f.write(text_ext) + + +if __name__ == '__main__': + from platform_methods import subprocess_main + subprocess_main(globals()) diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index cff19f990c..e2e71dda92 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const { if (size.z > max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const { if (size.z > max_size) { axis = 2; - max_size = size.z; } return axis; @@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const { if (size.z < max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const { if (size.z < max_size) { axis = 2; - max_size = size.z; } return axis; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index b1424e1d78..24096de551 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int idx = node_count - 1; - int steps = 0; while (true) { @@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { return true; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif + bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); idx = over ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif - - steps++; } return false; @@ -453,10 +449,10 @@ BSP_Tree::operator Variant() const { for (int i = 0; i < planes.size(); i++) { - plane_values[i * 4 + 0] = planes[i].normal.x; - plane_values[i * 4 + 1] = planes[i].normal.y; - plane_values[i * 4 + 2] = planes[i].normal.z; - plane_values[i * 4 + 3] = planes[i].d; + plane_values.write[i * 4 + 0] = planes[i].normal.x; + plane_values.write[i * 4 + 1] = planes[i].normal.y; + plane_values.write[i * 4 + 2] = planes[i].normal.z; + plane_values.write[i * 4 + 3] = planes[i].d; } d["planes"] = plane_values; @@ -502,10 +498,10 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { PoolVector<real_t>::Read r = src_planes.read(); for (int i = 0; i < plane_count / 4; i++) { - planes[i].normal.x = r[i * 4 + 0]; - planes[i].normal.y = r[i * 4 + 1]; - planes[i].normal.z = r[i * 4 + 2]; - planes[i].d = r[i * 4 + 3]; + planes.write[i].normal.x = r[i * 4 + 0]; + planes.write[i].normal.y = r[i * 4 + 1]; + planes.write[i].normal.z = r[i * 4 + 2]; + planes.write[i].d = r[i * 4 + 3]; } } @@ -524,9 +520,9 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { for (int i = 0; i < nodes.size(); i++) { - nodes[i].over = r[i * 3 + 0]; - nodes[i].under = r[i * 3 + 1]; - nodes[i].plane = r[i * 3 + 2]; + nodes.write[i].over = r[i * 3 + 0]; + nodes.write[i].under = r[i * 3 + 1]; + nodes.write[i].plane = r[i * 3 + 2]; } } diff --git a/core/math/delaunay.h b/core/math/delaunay.h index 09aebc773f..13fbc0c6ae 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -92,7 +92,7 @@ public: for (int j = 0; j < triangles.size(); j++) { if (circum_circle_contains(points, triangles[j], i)) { - triangles[j].bad = true; + triangles.write[j].bad = true; polygon.push_back(Edge(triangles[j].points[0], triangles[j].points[1])); polygon.push_back(Edge(triangles[j].points[1], triangles[j].points[2])); polygon.push_back(Edge(triangles[j].points[2], triangles[j].points[0])); @@ -109,8 +109,8 @@ public: for (int j = 0; j < polygon.size(); j++) { for (int k = j + 1; k < polygon.size(); k++) { if (edge_compare(points, polygon[j], polygon[k])) { - polygon[j].bad = true; - polygon[k].bad = true; + polygon.write[j].bad = true; + polygon.write[k].bad = true; } } } diff --git a/core/math/expression.cpp b/core/math/expression.cpp new file mode 100644 index 0000000000..a161dbddba --- /dev/null +++ b/core/math/expression.cpp @@ -0,0 +1,2136 @@ +#include "expression.h" + +#include "class_db.h" +#include "func_ref.h" +#include "io/marshalls.h" +#include "math_funcs.h" +#include "os/os.h" +#include "reference.h" +#include "variant_parser.h" + +const char *Expression::func_name[Expression::FUNC_MAX] = { + "sin", + "cos", + "tan", + "sinh", + "cosh", + "tanh", + "asin", + "acos", + "atan", + "atan2", + "sqrt", + "fmod", + "fposmod", + "floor", + "ceil", + "round", + "abs", + "sign", + "pow", + "log", + "exp", + "is_nan", + "is_inf", + "ease", + "decimals", + "stepify", + "lerp", + "inverse_lerp", + "range_lerp", + "dectime", + "randomize", + "randi", + "randf", + "rand_range", + "seed", + "rand_seed", + "deg2rad", + "rad2deg", + "linear2db", + "db2linear", + "polar2cartesian", + "cartesian2polar", + "wrapi", + "wrapf", + "max", + "min", + "clamp", + "nearest_po2", + "weakref", + "funcref", + "convert", + "typeof", + "type_exists", + "char", + "str", + "print", + "printerr", + "printraw", + "var2str", + "str2var", + "var2bytes", + "bytes2var", + "color_named", +}; + +Expression::BuiltinFunc Expression::find_function(const String &p_string) { + + for (int i = 0; i < FUNC_MAX; i++) { + if (p_string == func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String Expression::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); + return func_name[p_func]; +} + +int Expression::get_func_argument_count(BuiltinFunc p_func) { + + switch (p_func) { + + case MATH_RANDOMIZE: + case MATH_RAND: + case MATH_RANDF: + return 0; + case MATH_SIN: + case MATH_COS: + case MATH_TAN: + case MATH_SINH: + case MATH_COSH: + case MATH_TANH: + case MATH_ASIN: + case MATH_ACOS: + case MATH_ATAN: + case MATH_SQRT: + case MATH_FLOOR: + case MATH_CEIL: + case MATH_ROUND: + case MATH_ABS: + case MATH_SIGN: + case MATH_LOG: + case MATH_EXP: + case MATH_ISNAN: + case MATH_ISINF: + case MATH_DECIMALS: + case MATH_SEED: + case MATH_RANDSEED: + case MATH_DEG2RAD: + case MATH_RAD2DEG: + case MATH_LINEAR2DB: + case MATH_DB2LINEAR: + case LOGIC_NEAREST_PO2: + case OBJ_WEAKREF: + case TYPE_OF: + case TEXT_CHAR: + case TEXT_STR: + case TEXT_PRINT: + case TEXT_PRINTERR: + case TEXT_PRINTRAW: + case VAR_TO_STR: + case STR_TO_VAR: + case VAR_TO_BYTES: + case BYTES_TO_VAR: + case TYPE_EXISTS: + return 1; + case MATH_ATAN2: + case MATH_FMOD: + case MATH_FPOSMOD: + case MATH_POW: + case MATH_EASE: + case MATH_STEPIFY: + case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: + case LOGIC_MAX: + case LOGIC_MIN: + case FUNC_FUNCREF: + case TYPE_CONVERT: + case COLORN: + return 2; + case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_DECTIME: + case MATH_WRAP: + case MATH_WRAPF: + case LOGIC_CLAMP: + return 3; + case MATH_RANGE_LERP: + return 5; + case FUNC_MAX: { + } + } + return 0; +} + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_inputs[m_arg]->is_num()) { \ + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::REAL; \ + return; \ + } + +void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str) { + + switch (p_func) { + case MATH_SIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sin((double)*p_inputs[0]); + } break; + case MATH_COS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cos((double)*p_inputs[0]); + } break; + case MATH_TAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tan((double)*p_inputs[0]); + } break; + case MATH_SINH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sinh((double)*p_inputs[0]); + } break; + case MATH_COSH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cosh((double)*p_inputs[0]); + } break; + case MATH_TANH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tanh((double)*p_inputs[0]); + } break; + case MATH_ASIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::asin((double)*p_inputs[0]); + } break; + case MATH_ACOS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::acos((double)*p_inputs[0]); + } break; + case MATH_ATAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::atan((double)*p_inputs[0]); + } break; + case MATH_ATAN2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SQRT: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sqrt((double)*p_inputs[0]); + } break; + case MATH_FMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FPOSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FLOOR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::floor((double)*p_inputs[0]); + } break; + case MATH_CEIL: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::ceil((double)*p_inputs[0]); + } break; + case MATH_ROUND: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::round((double)*p_inputs[0]); + } break; + case MATH_ABS: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = ABS(i); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = Math::abs(r); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_SIGN: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_POW: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LOG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::log((double)*p_inputs[0]); + } break; + case MATH_EXP: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::exp((double)*p_inputs[0]); + } break; + case MATH_ISNAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_nan((double)*p_inputs[0]); + } break; + case MATH_ISINF: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_inf((double)*p_inputs[0]); + } break; + case MATH_EASE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; + case MATH_STEPIFY: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; + case MATH_DECTIME: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANDOMIZE: { + Math::randomize(); + + } break; + case MATH_RAND: { + *r_return = Math::rand(); + } break; + case MATH_RANDF: { + *r_return = Math::randf(); + } break; + case MATH_RANDOM: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + Math::seed(seed); + + } break; + case MATH_RANDSEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return = reta; + + } break; + case MATH_DEG2RAD: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::deg2rad((double)*p_inputs[0]); + } break; + case MATH_RAD2DEG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::rad2deg((double)*p_inputs[0]); + } break; + case MATH_LINEAR2DB: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::linear2db((double)*p_inputs[0]); + } break; + case MATH_DB2LINEAR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::db2linear((double)*p_inputs[0]); + } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; + case MATH_WRAP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); + } break; + case MATH_WRAPF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case LOGIC_MAX: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MAX(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MAX(a, b); + } + + } break; + case LOGIC_MIN: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MIN(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MIN(a, b); + } + } break; + case LOGIC_CLAMP: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return = CLAMP(a, b, c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; + + *r_return = CLAMP(a, b, c); + } + } break; + case LOGIC_NEAREST_PO2: { + + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = next_power_of_2(num); + } break; + case OBJ_WEAKREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + + if (p_inputs[0]->is_ref()) { + + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; + } + + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_ref(r); + *r_return = wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { + + return; + } + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_obj(obj); + *r_return = wref; + } + + } break; + case FUNC_FUNCREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING; + + return; + } + + Ref<FuncRef> fr = memnew(FuncRef); + + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); + + *r_return = fr; + + } break; + case TYPE_CONVERT: { + + VALIDATE_ARG_NUM(1); + int type = *p_inputs[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + + r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + + *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); + } + } break; + case TYPE_OF: { + + *r_return = p_inputs[0]->get_type(); + + } break; + case TYPE_EXISTS: { + + *r_return = ClassDB::class_exists(*p_inputs[0]); + + } break; + case TEXT_CHAR: { + + CharType result[2] = { *p_inputs[0], 0 }; + + *r_return = String(result); + + } break; + case TEXT_STR: { + + String str = *p_inputs[0]; + + *r_return = str; + + } break; + case TEXT_PRINT: { + + String str = *p_inputs[0]; + print_line(str); + + } break; + + case TEXT_PRINTERR: { + + String str = *p_inputs[0]; + + //str+="\n"; + print_error(str); + + } break; + case TEXT_PRINTRAW: { + String str = *p_inputs[0]; + + //str+="\n"; + OS::get_singleton()->print("%s", str.utf8().get_data()); + + } break; + case VAR_TO_STR: { + + String vars; + VariantWriter::write_to_string(*p_inputs[0], vars); + *r_return = vars; + } break; + case STR_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + + return; + } + + VariantParser::StreamString ss; + ss.s = *p_inputs[0]; + + String errs; + int line; + Error err = VariantParser::parse(&ss, *r_return, errs, line); + + if (err != OK) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_return = "Parse error at line " + itos(line) + ": " + errs; + return; + } + + } break; + case VAR_TO_BYTES: { + + PoolByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0], NULL, len); + if (err) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0], w.ptr(), len); + } + *r_return = barr; + } break; + case BYTES_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::POOL_BYTE_ARRAY) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + + return; + } + + PoolByteArray varr = *p_inputs[0]; + Variant ret; + { + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + return; + } + } + + *r_return = ret; + + } break; + case COLORN: { + + VALIDATE_ARG_NUM(1); + + Color color = Color::named(*p_inputs[0]); + color.a = *p_inputs[1]; + + *r_return = String(color); + + } break; + default: {} + } +} + +//////// + +Error Expression::_get_token(Token &r_token) { + + while (true) { +#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar == 0) { + r_token.type = TK_EOF; + return OK; + } + + switch (cchar) { + + case 0: { + r_token.type = TK_EOF; + return OK; + } break; + case '{': { + + r_token.type = TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type = TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type = TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type = TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type = TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type = TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type = TK_COMMA; + return OK; + }; + case ':': { + + r_token.type = TK_COLON; + return OK; + }; + case '.': { + + r_token.type = TK_PERIOD; + return OK; + }; + case '$': { + + r_token.type = TK_INPUT; + int index = 0; + do { + if (expression[str_ofs] < '0' || expression[str_ofs] > '9') { + _set_error("Expected number after '$'"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + index *= 10; + index += expression[str_ofs] - '0'; + str_ofs++; + + } while (expression[str_ofs] >= '0' && expression[str_ofs] <= '9'); + + r_token.value = index; + return OK; + }; + case '=': { + + cchar = GET_CHAR(); + if (cchar == '=') { + r_token.type = TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type = TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '>') { + r_token.type = TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type = TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '<') { + r_token.type = TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type = TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type = TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type = TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type = TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type = TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type = TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs] == '&') { + r_token.type = TK_OP_AND; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs] == '|') { + r_token.type = TK_OP_OR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type = TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type = TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + String str; + while (true) { + + CharType ch = GET_CHAR(); + + if (ch == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch == '"') { + break; + } else if (ch == '\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res = 0; + + switch (next) { + + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': res = 13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + for (int j = 0; j < 4; j++) { + CharType c = GET_CHAR(); + + if (c == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + + _set_error("Malformed hex constant in string"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType v; + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + ERR_PRINT("BUG"); + v = 0; + } + + res <<= 4; + res |= v; + } + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str += res; + + } else { + str += ch; + } + } + + r_token.type = TK_CONSTANT; + r_token.value = str; + return OK; + + } break; + default: { + + if (cchar <= 32) { + break; + } + + if (cchar >= '0' && cchar <= '9') { + //a number + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading = READING_INT; + + CharType c = cchar; + bool exp_sign = false; + bool exp_beg = false; + bool is_float = false; + + while (true) { + + switch (reading) { + case READING_INT: { + + if (c >= '0' && c <= '9') { + //pass + } else if (c == '.') { + reading = READING_DEC; + is_float = true; + } else if (c == 'e') { + reading = READING_EXP; + } else { + reading = READING_DONE; + } + + } break; + case READING_DEC: { + + if (c >= '0' && c <= '9') { + + } else if (c == 'e') { + reading = READING_EXP; + + } else { + reading = READING_DONE; + } + + } break; + case READING_EXP: { + + if (c >= '0' && c <= '9') { + exp_beg = true; + + } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { + if (c == '-') + is_float = true; + exp_sign = true; + + } else { + reading = READING_DONE; + } + } break; + } + + if (reading == READING_DONE) + break; + num += String::chr(c); + c = GET_CHAR(); + } + + str_ofs--; + + r_token.type = TK_CONSTANT; + + if (is_float) + r_token.value = num.to_double(); + else + r_token.value = num.to_int(); + return OK; + + } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + + String id; + bool first = true; + + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + + id += String::chr(cchar); + cchar = GET_CHAR(); + first = false; + } + + str_ofs--; //go back one + + if (id == "in") { + r_token.type = TK_OP_IN; + } else if (id == "null") { + r_token.type = TK_CONSTANT; + r_token.value = Variant(); + } else if (id == "true") { + r_token.type = TK_CONSTANT; + r_token.value = true; + } else if (id == "false") { + r_token.type = TK_CONSTANT; + r_token.value = false; + } else if (id == "PI") { + r_token.type = TK_CONSTANT; + r_token.value = Math_PI; + } else if (id == "TAU") { + r_token.type = TK_CONSTANT; + r_token.value = Math_TAU; + } else if (id == "INF") { + r_token.type = TK_CONSTANT; + r_token.value = Math_INF; + } else if (id == "NAN") { + r_token.type = TK_CONSTANT; + r_token.value = Math_NAN; + } else if (id == "not") { + r_token.type = TK_OP_NOT; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "and") { + r_token.type = TK_OP_AND; + } else if (id == "self") { + r_token.type = TK_SELF; + } else { + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (id == Variant::get_type_name(Variant::Type(i))) { + r_token.type = TK_BASIC_TYPE; + r_token.value = i; + return OK; + } + } + + BuiltinFunc bifunc = find_function(id); + if (bifunc != FUNC_MAX) { + r_token.type = TK_BUILTIN_FUNC; + r_token.value = bifunc; + return OK; + } + + r_token.type = TK_IDENTIFIER; + r_token.value = id; + } + + return OK; + } else { + _set_error("Unexpected character."); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } + } + } + + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char *Expression::token_name[TK_MAX] = { + "CURLY BRACKET OPEN", + "CURLY BRACKET CLOSE", + "BRACKET OPEN", + "BRACKET CLOSE", + "PARENTHESIS OPEN", + "PARENTHESIS CLOSE", + "IDENTIFIER", + "BUILTIN FUNC", + "SELF", + "CONSTANT", + "BASIC TYPE", + "COLON", + "COMMA", + "PERIOD", + "OP IN", + "OP EQUAL", + "OP NOT EQUAL", + "OP LESS", + "OP LESS EQUAL", + "OP GREATER", + "OP GREATER EQUAL", + "OP AND", + "OP OR", + "OP NOT", + "OP ADD", + "OP SUB", + "OP MUL", + "OP DIV", + "OP MOD", + "OP SHIFT LEFT", + "OP SHIFT RIGHT", + "OP BIT AND", + "OP BIT OR", + "OP BIT XOR", + "OP BIT INVERT", + "OP INPUT", + "EOF", + "ERROR" +}; + +Expression::ENode *Expression::_parse_expression() { + + Vector<ExpressionNode> expression; + + while (true) { + //keep appending stuff to expression + ENode *expr = NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + switch (tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node<DictionaryNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr = _parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr = dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node<ArrayNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr = an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode *e = _parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = e; + + } break; + case TK_IDENTIFIER: { + + String identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + SelfNode *self_node = alloc_node<SelfNode>(); + func_call->base = self_node; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + int input_index = -1; + for (int i = 0; i < input_names.size(); i++) { + if (input_names[i] == identifier) { + input_index = i; + break; + } + } + + if (input_index != -1) { + InputNode *input = alloc_node<InputNode>(); + input->index = input_index; + expr = input; + } else { + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + SelfNode *self_node = alloc_node<SelfNode>(); + index->base = self_node; + index->name = identifier; + expr = index; + } + } + } break; + case TK_INPUT: { + + InputNode *input = alloc_node<InputNode>(); + input->index = tk.value; + expr = input; + } break; + case TK_SELF: { + + SelfNode *self = alloc_node<SelfNode>(); + expr = self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = tk.value; + expr = constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node<ConstructorNode>(); + constructor->data_type = bt; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); + bifunc->func = BuiltinFunc(int(tk.value)); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } + + expr = bifunc; + + } break; + case TK_OP_SUB: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + } + + //before going to operators, must check indexing! + + while (true) { + int cofs2 = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done = false; + + switch (tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node<IndexNode>(); + index->base = expr; + + ENode *what = _parse_expression(); + if (!what) + return NULL; + + index->index = what; + + _get_token(tk); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr = index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + func_call->base = expr; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + index->base = expr; + index->name = identifier; + expr = index; + } + + } break; + default: { + str_ofs = cofs2; + done = true; + } break; + } + + if (done) + break; + } + + //push expression + { + ExpressionNode e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + } + + //ok finally look for an operator + + int cofs = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + Variant::Operator op = Variant::OP_MAX; + + switch (tk.type) { + case TK_OP_IN: op = Variant::OP_IN; break; + case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op = Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op = Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op = Variant::OP_AND; break; + case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_NOT: op = Variant::OP_NOT; break; + case TK_OP_ADD: op = Variant::OP_ADD; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; + case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op = Variant::OP_DIVIDE; break; + case TK_OP_MOD: op = Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op == Variant::OP_MAX) { //stop appending stuff + str_ofs = cofs; + break; + } + + //push operator and go on + { + ExpressionNode e; + e.is_op = true; + e.op = op; + expression.push_back(e); + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary = false; + + switch (expression[i].op) { + + case Variant::OP_BIT_NEGATE: + priority = 0; + unary = true; + break; + case Variant::OP_NEGATE: + priority = 1; + unary = true; + break; + + case Variant::OP_MULTIPLY: priority = 2; break; + case Variant::OP_DIVIDE: priority = 2; break; + case Variant::OP_MODULE: priority = 2; break; + + case Variant::OP_ADD: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; + + case Variant::OP_SHIFT_LEFT: priority = 4; break; + case Variant::OP_SHIFT_RIGHT: priority = 4; break; + + case Variant::OP_BIT_AND: priority = 5; break; + case Variant::OP_BIT_XOR: priority = 6; break; + case Variant::OP_BIT_OR: priority = 7; break; + + case Variant::OP_LESS: priority = 8; break; + case Variant::OP_LESS_EQUAL: priority = 8; break; + case Variant::OP_GREATER: priority = 8; break; + case Variant::OP_GREATER_EQUAL: priority = 8; break; + + case Variant::OP_EQUAL: priority = 8; break; + case Variant::OP_NOT_EQUAL: priority = 8; break; + + case Variant::OP_IN: priority = 10; break; + + case Variant::OP_NOT: + priority = 11; + unary = true; + break; + case Variant::OP_AND: priority = 12; break; + case Variant::OP_OR: priority = 13; break; + + default: { + _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); + return NULL; + } + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op = i; + min_priority = priority; + is_unary = unary; + } + } + + if (next_op == -1) { + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op == -1, NULL); + } + + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary opeators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[i].op; + op->nodes[0] = expression[i + 1].node; + op->nodes[1] = NULL; + expression.write[i].is_op = false; + expression.write[i].node = op; + expression.remove(i + 1); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + op->nodes[0] = expression[next_op - 1].node; //expression goes as left + op->nodes[1] = expression[next_op + 1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression.write[next_op - 1].node = op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool Expression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return true; + } + + expression_dirty = false; + return false; +} + +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { + + switch (p_node->type) { + case Expression::ENode::TYPE_INPUT: { + + const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); + if (in->index < 0 || in->index >= p_inputs.size()) { + r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); + return true; + } + r_ret = p_inputs[in->index]; + } break; + case Expression::ENode::TYPE_CONSTANT: { + + const Expression::ConstantNode *c = static_cast<const Expression::ConstantNode *>(p_node); + r_ret = c->value; + + } break; + case Expression::ENode::TYPE_SELF: { + + if (!p_instance) { + r_error_str = RTR("self can't be used because instance is null (not passed)"); + return true; + } + r_ret = p_instance; + } break; + case Expression::ENode::TYPE_OPERATOR: { + + const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node); + + Variant a; + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + if (ret) + return true; + } + + bool valid = true; + Variant::evaluate(op->op, a, b, r_ret, valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid operands to operator %s, %s and %s."), Variant::get_operator_name(op->op), Variant::get_type_name(a.get_type()), Variant::get_type_name(b.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_INDEX: { + + const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get(idx, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid index of type %s for base type %s"), Variant::get_type_name(idx.get_type()), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_NAMED_INDEX: { + + const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get_named(index->name, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid named index '%s' for base type "), String(index->name), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_ARRAY: { + const Expression::ArrayNode *array = static_cast<const Expression::ArrayNode *>(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i = 0; i < array->array.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); + + if (ret) + return true; + arr[i] = value; + } + + r_ret = arr; + + } break; + case Expression::ENode::TYPE_DICTIONARY: { + const Expression::DictionaryNode *dictionary = static_cast<const Expression::DictionaryNode *>(p_node); + + Dictionary d; + for (int i = 0; i < dictionary->dict.size(); i += 2) { + + Variant key; + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); + + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + if (ret) + return true; + + d[key] = value; + } + + r_ret = d; + } break; + case Expression::ENode::TYPE_CONSTRUCTOR: { + + const Expression::ConstructorNode *constructor = static_cast<const Expression::ConstructorNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i = 0; i < constructor->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); + return true; + } + + } break; + case Expression::ENode::TYPE_BUILTIN_FUNC: { + + const Expression::BuiltinFuncNode *bifunc = static_cast<const Expression::BuiltinFuncNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i = 0; i < bifunc->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = "Builtin Call Failed. " + r_error_str; + return true; + } + + } break; + case Expression::ENode::TYPE_CALL: { + + const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); + + if (ret) + return true; + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i = 0; i < call->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); + return true; + } + + } break; + } + return false; +} + +Error Expression::parse(const String &p_expression, const Vector<String> &p_input_names) { + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + input_names = p_input_names; + + expression = p_expression; + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return ERR_INVALID_PARAMETER; + } + + return OK; +} + +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + + execution_error = false; + Variant output; + String error_txt; + bool err = _execute(p_inputs, p_base, root, output, error_txt); + if (err) { + execution_error = true; + error_str = error_txt; + if (p_show_error) { + ERR_EXPLAIN(error_str); + ERR_FAIL_V(Variant()); + } + } + + return output; +} + +bool Expression::has_execute_failed() const { + return execution_error; +} + +String Expression::get_error_text() const { + return error_str; +} + +void Expression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>())); + ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); + ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); +} + +Expression::Expression() { + output_type = Variant::NIL; + error_set = true; + root = NULL; + nodes = NULL; + sequenced = false; + execution_error = false; +} + +Expression::~Expression() { + + if (nodes) { + memdelete(nodes); + } +} diff --git a/core/math/expression.h b/core/math/expression.h new file mode 100644 index 0000000000..7a7639cf0b --- /dev/null +++ b/core/math/expression.h @@ -0,0 +1,325 @@ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include "core/reference.h" + +class Expression : public Reference { + GDCLASS(Expression, Reference) +public: + enum BuiltinFunc { + MATH_SIN, + MATH_COS, + MATH_TAN, + MATH_SINH, + MATH_COSH, + MATH_TANH, + MATH_ASIN, + MATH_ACOS, + MATH_ATAN, + MATH_ATAN2, + MATH_SQRT, + MATH_FMOD, + MATH_FPOSMOD, + MATH_FLOOR, + MATH_CEIL, + MATH_ROUND, + MATH_ABS, + MATH_SIGN, + MATH_POW, + MATH_LOG, + MATH_EXP, + MATH_ISNAN, + MATH_ISINF, + MATH_EASE, + MATH_DECIMALS, + MATH_STEPIFY, + MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, + MATH_DECTIME, + MATH_RANDOMIZE, + MATH_RAND, + MATH_RANDF, + MATH_RANDOM, + MATH_SEED, + MATH_RANDSEED, + MATH_DEG2RAD, + MATH_RAD2DEG, + MATH_LINEAR2DB, + MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, + MATH_WRAP, + MATH_WRAPF, + LOGIC_MAX, + LOGIC_MIN, + LOGIC_CLAMP, + LOGIC_NEAREST_PO2, + OBJ_WEAKREF, + FUNC_FUNCREF, + TYPE_CONVERT, + TYPE_OF, + TYPE_EXISTS, + TEXT_CHAR, + TEXT_STR, + TEXT_PRINT, + TEXT_PRINTERR, + TEXT_PRINTRAW, + VAR_TO_STR, + STR_TO_VAR, + VAR_TO_BYTES, + BYTES_TO_VAR, + COLORN, + FUNC_MAX + }; + + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str); + static BuiltinFunc find_function(const String &p_string); + +private: + static const char *func_name[FUNC_MAX]; + + struct Input { + + Variant::Type type; + String name; + + Input() { type = Variant::NIL; } + }; + + Vector<Input> inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_INPUT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + void _set_error(const String &p_err) { + if (error_set) + return; + error_str = p_err; + error_set = true; + } + + Error _get_token(Token &r_token); + + String error_str; + bool error_set; + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next = NULL; } + virtual ~ENode() { + if (next) { + memdelete(next); + } + } + }; + + struct ExpressionNode { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode *_parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type = TYPE_INPUT; + } + }; + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type = TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode *nodes[2]; + + OperatorNode() { + type = TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + SelfNode() { + type = TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode *base; + ENode *index; + + IndexNode() { + type = TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode *base; + StringName name; + + NamedIndexNode() { + type = TYPE_NAMED_INDEX; + } + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector<ENode *> arguments; + + ConstructorNode() { + type = TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode *base; + StringName method; + Vector<ENode *> arguments; + + CallNode() { + type = TYPE_CALL; + } + }; + + struct ArrayNode : public ENode { + Vector<ENode *> array; + ArrayNode() { + type = TYPE_ARRAY; + } + }; + + struct DictionaryNode : public ENode { + Vector<ENode *> dict; + DictionaryNode() { + type = TYPE_DICTIONARY; + } + }; + + struct BuiltinFuncNode : public ENode { + BuiltinFunc func; + Vector<ENode *> arguments; + BuiltinFuncNode() { + type = TYPE_BUILTIN_FUNC; + } + }; + + template <class T> + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + ENode *root; + ENode *nodes; + + Vector<String> input_names; + + bool execution_error; + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + +protected: + static void _bind_methods(); + +public: + Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); + Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true); + bool has_execute_failed() const; + String get_error_text() const; + + Expression(); + ~Expression(); +}; + +#endif // EXPRESSION_H diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 24f077a4ca..7ab28daf20 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -56,7 +56,7 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[idx] = ni; } - faces[i].indices[j] = vtx_remap[idx]; + faces.write[i].indices.write[j] = vtx_remap[idx]; } } @@ -74,8 +74,8 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[b] = ni; } - edges[i].a = vtx_remap[a]; - edges[i].b = vtx_remap[b]; + edges.write[i].a = vtx_remap[a]; + edges.write[i].b = vtx_remap[b]; } Vector<Vector3> new_vertices; @@ -84,7 +84,7 @@ void Geometry::MeshData::optimize_vertices() { for (int i = 0; i < vertices.size(); i++) { if (vtx_remap.has(i)) - new_vertices[vtx_remap[i]] = vertices[i]; + new_vertices.write[vtx_remap[i]] = vertices[i]; } vertices = new_vertices; } @@ -1014,8 +1014,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<_AtlasWorkRect> wrects; wrects.resize(p_rects.size()); for (int i = 0; i < p_rects.size(); i++) { - wrects[i].s = p_rects[i]; - wrects[i].idx = i; + wrects.write[i].s = p_rects[i]; + wrects.write[i].idx = i; } wrects.sort(); int widest = wrects[0].s.width; @@ -1033,7 +1033,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<int> hmax; hmax.resize(w); for (int j = 0; j < w; j++) - hmax[j] = 0; + hmax.write[j] = 0; //place them int ofs = 0; @@ -1052,8 +1052,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu from_y = hmax[ofs + k]; } - wrects[j].p.x = ofs; - wrects[j].p.y = from_y; + wrects.write[j].p.x = ofs; + wrects.write[j].p.y = from_y; int end_h = from_y + wrects[j].s.height; int end_w = ofs + wrects[j].s.width; if (ofs == 0) @@ -1061,7 +1061,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int k = 0; k < wrects[j].s.width; k++) { - hmax[ofs + k] = end_h; + hmax.write[ofs + k] = end_h; } if (end_h > max_h) @@ -1101,7 +1101,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int i = 0; i < p_rects.size(); i++) { - r_result[results[best].result[i].idx] = results[best].result[i].p; + r_result.write[results[best].result[i].idx] = results[best].result[i].p; } r_size = Size2(results[best].max_w, results[best].max_h); diff --git a/core/math/geometry.h b/core/math/geometry.h index be998aef0b..186a05fb37 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -890,14 +890,14 @@ public: for (int i = 0; i < n; ++i) { while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } // Build upper hull for (int i = n - 2, t = k + 1; i >= 0; i--) { while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } H.resize(k); diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index 2371f49561..7db41756ed 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -242,18 +242,11 @@ void Basis::scale_local(const Vector3 &p_scale) { Basis Basis::scaled_local(const Vector3 &p_scale) const { Basis b; - b.set_scale(p_scale); + b.set_diagonal(p_scale); return (*this) * b; } -void Basis::set_scale(const Vector3 &p_scale) { - - set_axis(0, get_axis(0).normalized() * p_scale.x); - set_axis(1, get_axis(1).normalized() * p_scale.y); - set_axis(2, get_axis(2).normalized() * p_scale.z); -} - Vector3 Basis::get_scale_abs() const { return Vector3( diff --git a/core/math/matrix3.h b/core/math/matrix3.h index cd1b51baa6..9ff1a97dc9 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -112,7 +112,6 @@ public: void scale_local(const Vector3 &p_scale); Basis scaled_local(const Vector3 &p_scale) const; - void set_scale(const Vector3 &p_scale); Vector3 get_scale() const; Vector3 get_scale_abs() const; Vector3 get_scale_local() const; @@ -232,10 +231,13 @@ public: operator Quat() const { return get_quat(); } Basis(const Quat &p_quat) { set_quat(p_quat); }; + Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } + Basis(const Vector3 &p_euler) { set_euler(p_euler); } + Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } + Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } - Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fc90417413..cb923d264e 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -61,10 +61,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { - valid_points[i] = false; + valid_points.write[i] = false; //print_line("INVALIDATED: "+itos(i)); } else { - valid_points[i] = true; + valid_points.write[i] = true; valid_cache.insert(sp); } } @@ -452,7 +452,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me int idx = 0; for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { - r_mesh.faces[idx++] = E->get(); + r_mesh.faces.write[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; @@ -461,7 +461,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Geometry::MeshData::Edge e; e.a = E->key().vertices[0]; e.b = E->key().vertices[1]; - r_mesh.edges[idx++] = e; + r_mesh.edges.write[idx++] = e; } r_mesh.vertices = p_points; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index d1e190f4b9..976e0f174e 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -127,12 +127,11 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) Quat dst_rot = p_transform.basis.get_rotation_quat(); Vector3 dst_loc = p_transform.origin; - Transform dst; //this could be made faster by using a single function in Basis.. - dst.basis = src_rot.slerp(dst_rot, p_c).normalized(); - dst.basis.set_scale(src_scale.linear_interpolate(dst_scale, p_c)); - dst.origin = src_loc.linear_interpolate(dst_loc, p_c); + Transform interp; + interp.basis.set_quat_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); - return dst; + return interp; } void Transform::scale(const Vector3 &p_scale) { diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 563ba7268f..0edc0ea039 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -128,10 +128,10 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul if (0.0 < get_area(contour)) for (int v = 0; v < n; v++) - V[v] = v; + V.write[v] = v; else for (int v = 0; v < n; v++) - V[v] = (n - 1) - v; + V.write[v] = (n - 1) - v; bool relaxed = false; @@ -182,7 +182,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul /* remove v from remaining polygon */ for (s = v, t = v + 1; t < nv; s++, t++) - V[s] = V[t]; + V.write[s] = V[t]; nv--; diff --git a/core/method_bind.h b/core/method_bind.h index 41b500c401..7ee687ee40 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -354,7 +354,7 @@ public: for (int i = 0; i < p_info.arguments.size(); i++) { at[i + 1] = p_info.arguments[i].type; - names[i] = p_info.arguments[i].name; + names.write[i] = p_info.arguments[i].name; } set_argument_names(names); diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 677e8e1fb2..2f6dcb3178 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -180,7 +180,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -207,7 +207,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -225,7 +225,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -252,7 +252,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -277,7 +277,7 @@ MAKE_VECARG_ALT(String, StringName); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ @@ -298,7 +298,7 @@ MAKE_VECARG_ALT(String, StringName); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ diff --git a/core/node_path.cpp b/core/node_path.cpp index 487d5ee8c6..7d4116fa1e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -427,7 +427,7 @@ NodePath::NodePath(const String &p_path) { String name = path.substr(from, i - from); ERR_FAIL_INDEX(slice, data->path.size()); - data->path[slice++] = name; + data->path.write[slice++] = name; } from = i + 1; last_is_slash = true; diff --git a/core/object.cpp b/core/object.cpp index d86c60a3b8..a0c64feb09 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -818,8 +818,8 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { argptrs.resize(p_args.size()); for (int i = 0; i < p_args.size(); i++) { - args[i] = p_args[i]; - argptrs[i] = &args[i]; + args.write[i] = p_args[i]; + argptrs.write[i] = &args[i]; } Variant::CallError ce; @@ -1182,10 +1182,10 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int bind_mem.resize(p_argcount + c.binds.size()); for (int j = 0; j < p_argcount; j++) { - bind_mem[j] = p_args[j]; + bind_mem.write[j] = p_args[j]; } for (int j = 0; j < c.binds.size(); j++) { - bind_mem[p_argcount + j] = &c.binds[j]; + bind_mem.write[p_argcount + j] = &c.binds[j]; } args = (const Variant **)bind_mem.ptr(); @@ -1209,7 +1209,15 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } } - if (c.flags & CONNECT_ONESHOT) { + bool disconnect = c.flags & CONNECT_ONESHOT; +#ifdef TOOLS_ENABLED + if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { + //this signal was connected from the editor, and is being edited. just dont disconnect for now + disconnect = false; + } +#endif + if (disconnect) { + _ObjectSignalDisconnectData dd; dd.signal = p_name; dd.target = target; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 033b4b12b9..59f07c03e7 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -262,15 +262,14 @@ String FileAccess::get_token() const { while (!eof_reached()) { if (c <= ' ') { - if (!token.empty()) + if (token.length()) break; } else { - token.push_back(c); + token += c; } c = get_8(); } - token.push_back(0); return String::utf8(token.get_data()); } @@ -293,7 +292,7 @@ class CharBuffer { for (int i = 0; i < written; i++) { - vector[i] = stack_buffer[i]; + vector.write[i] = stack_buffer[i]; } } diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index ca6446d015..12c6ef7d3b 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -949,6 +949,14 @@ bool InputEventAction::is_pressed() const { return pressed; } +bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const { + Ref<InputEventKey> event = p_event; + if (event.is_null()) + return false; + + return event->is_action(action); +} + bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; @@ -1080,3 +1088,122 @@ InputEventPanGesture::InputEventPanGesture() { delta = Vector2(0, 0); } +///////////////////////////// + +void InputEventMIDI::set_channel(const int p_channel) { + + channel = p_channel; +} + +int InputEventMIDI::get_channel() const { + return channel; +} + +void InputEventMIDI::set_message(const int p_message) { + + message = p_message; +} + +int InputEventMIDI::get_message() const { + return message; +} + +void InputEventMIDI::set_pitch(const int p_pitch) { + + pitch = p_pitch; +} + +int InputEventMIDI::get_pitch() const { + return pitch; +} + +void InputEventMIDI::set_velocity(const int p_velocity) { + + velocity = p_velocity; +} + +int InputEventMIDI::get_velocity() const { + return velocity; +} + +void InputEventMIDI::set_instrument(const int p_instrument) { + + instrument = p_instrument; +} + +int InputEventMIDI::get_instrument() const { + return instrument; +} + +void InputEventMIDI::set_pressure(const int p_pressure) { + + pressure = p_pressure; +} + +int InputEventMIDI::get_pressure() const { + return pressure; +} + +void InputEventMIDI::set_controller_number(const int p_controller_number) { + + controller_number = p_controller_number; +} + +int InputEventMIDI::get_controller_number() const { + return controller_number; +} + +void InputEventMIDI::set_controller_value(const int p_controller_value) { + + controller_value = p_controller_value; +} + +int InputEventMIDI::get_controller_value() const { + return controller_value; +} + +String InputEventMIDI::as_text() const { + + return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")"; +} + +void InputEventMIDI::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel); + ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel); + ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message); + ClassDB::bind_method(D_METHOD("get_message"), &InputEventMIDI::get_message); + ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &InputEventMIDI::set_pitch); + ClassDB::bind_method(D_METHOD("get_pitch"), &InputEventMIDI::get_pitch); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMIDI::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMIDI::get_velocity); + ClassDB::bind_method(D_METHOD("set_instrument", "instrument"), &InputEventMIDI::set_instrument); + ClassDB::bind_method(D_METHOD("get_instrument"), &InputEventMIDI::get_instrument); + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMIDI::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMIDI::get_pressure); + ClassDB::bind_method(D_METHOD("set_controller_number", "controller_number"), &InputEventMIDI::set_controller_number); + ClassDB::bind_method(D_METHOD("get_controller_number"), &InputEventMIDI::get_controller_number); + ClassDB::bind_method(D_METHOD("set_controller_value", "controller_value"), &InputEventMIDI::set_controller_value); + ClassDB::bind_method(D_METHOD("get_controller_value"), &InputEventMIDI::get_controller_value); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "channel"), "set_channel", "get_channel"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "message"), "set_message", "get_message"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pitch"), "set_pitch", "get_pitch"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "velocity"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "instrument"), "set_instrument", "get_instrument"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pressure"), "set_pressure", "get_pressure"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); +} + +InputEventMIDI::InputEventMIDI() { + + channel = 0; + message = 0; + pitch = 0; + velocity = 0; + instrument = 0; + pressure = 0; + controller_number = 0; + controller_value = 0; +} diff --git a/core/os/input_event.h b/core/os/input_event.h index bd1a85ce29..07df81488b 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -140,6 +140,16 @@ enum JoystickList { JOY_ANALOG_R2 = JOY_AXIS_7, }; +enum MidiMessageList { + MIDI_MESSAGE_NOTE_OFF = 0x8, + MIDI_MESSAGE_NOTE_ON = 0x9, + MIDI_MESSAGE_AFTERTOUCH = 0xA, + MIDI_MESSAGE_CONTROL_CHANGE = 0xB, + MIDI_MESSAGE_PROGRAM_CHANGE = 0xC, + MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD, + MIDI_MESSAGE_PITCH_BEND = 0xE, +}; + /** * Input Modifier Status * for keyboard/mouse events. @@ -470,6 +480,7 @@ public: virtual bool is_action(const StringName &p_action) const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; @@ -525,4 +536,50 @@ public: InputEventPanGesture(); }; + +class InputEventMIDI : public InputEvent { + GDCLASS(InputEventMIDI, InputEvent) + + int channel; + int message; + int pitch; + int velocity; + int instrument; + int pressure; + int controller_number; + int controller_value; + +protected: + static void _bind_methods(); + +public: + void set_channel(const int p_channel); + int get_channel() const; + + void set_message(const int p_message); + int get_message() const; + + void set_pitch(const int p_pitch); + int get_pitch() const; + + void set_velocity(const int p_velocity); + int get_velocity() const; + + void set_instrument(const int p_instrument); + int get_instrument() const; + + void set_pressure(const int p_pressure); + int get_pressure() const; + + void set_controller_number(const int p_controller_number); + int get_controller_number() const; + + void set_controller_value(const int p_controller_value); + int get_controller_value() const; + + virtual String as_text() const; + + InputEventMIDI(); +}; + #endif diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp new file mode 100644 index 0000000000..7b4f84473c --- /dev/null +++ b/core/os/midi_driver.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* midi_driver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "midi_driver.h" + +#include "main/input_default.h" +#include "os/os.h" + +MIDIDriver *MIDIDriver::singleton = NULL; +MIDIDriver *MIDIDriver::get_singleton() { + + return singleton; +} + +void MIDIDriver::set_singleton() { + + singleton = this; +} + +void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { + + Ref<InputEventMIDI> event; + event.instance(); + + if (length >= 1) { + event->set_channel(data[0] & 0xF); + event->set_message(data[0] >> 4); + } + + switch (event->get_message()) { + case MIDI_MESSAGE_AFTERTOUCH: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_pressure(data[2]); + } + break; + + case MIDI_MESSAGE_CONTROL_CHANGE: + if (length >= 3) { + event->set_controller_number(data[1]); + event->set_controller_value(data[2]); + } + break; + + case MIDI_MESSAGE_NOTE_ON: + case MIDI_MESSAGE_NOTE_OFF: + case MIDI_MESSAGE_PITCH_BEND: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_velocity(data[2]); + } + break; + + case MIDI_MESSAGE_PROGRAM_CHANGE: + if (length >= 2) { + event->set_instrument(data[1]); + } + break; + + case MIDI_MESSAGE_CHANNEL_PRESSURE: + if (length >= 2) { + event->set_pressure(data[1]); + } + break; + } + + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + id->parse_input_event(event); +} + +PoolStringArray MIDIDriver::get_connected_inputs() { + + PoolStringArray list; + return list; +} + +MIDIDriver::MIDIDriver() { + + set_singleton(); +} diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h new file mode 100644 index 0000000000..1a3a67a411 --- /dev/null +++ b/core/os/midi_driver.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* midi_driver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MIDI_DRIVER_H +#define MIDI_DRIVER_H + +#include "core/variant.h" +#include "typedefs.h" +/** + * Multi-Platform abstraction for accessing to MIDI. + */ + +class MIDIDriver { + + static MIDIDriver *singleton; + +public: + static MIDIDriver *get_singleton(); + void set_singleton(); + + virtual Error open() = 0; + virtual void close() = 0; + + virtual PoolStringArray get_connected_inputs(); + + static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length); + + MIDIDriver(); + virtual ~MIDIDriver() {} +}; + +#endif diff --git a/core/os/os.cpp b/core/os/os.cpp index 89866e4044..97dae05919 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -33,6 +33,7 @@ #include "dir_access.h" #include "input.h" #include "os/file_access.h" +#include "os/midi_driver.h" #include "project_settings.h" #include "servers/audio_server.h" #include "version_generated.gen.h" @@ -576,6 +577,13 @@ bool OS::has_feature(const String &p_feature) { if (p_feature == "release") return true; #endif +#ifdef TOOLS_ENABLED + if (p_feature == "editor") + return true; +#else + if (p_feature == "standalone") + return true; +#endif if (sizeof(void *) == 8 && p_feature == "64") { return true; @@ -672,6 +680,15 @@ List<String> OS::get_restart_on_exit_arguments() const { return restart_commandline; } +PoolStringArray OS::get_connected_midi_inputs() { + + if (MIDIDriver::get_singleton()) + return MIDIDriver::get_singleton()->get_connected_inputs(); + + PoolStringArray list; + return list; +} + OS::OS() { void *volatile stack_bottom; diff --git a/core/os/os.h b/core/os/os.h index 4e0cb003fb..dd783408e8 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -189,6 +189,8 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const { return 1; } virtual int get_current_screen() const { return 0; } virtual void set_current_screen(int p_screen) {} diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index e0fb589767..3ff7db2a44 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -80,7 +80,7 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us threads.resize(OS::get_singleton()->get_processor_count()); for (int i = 0; i < threads.size(); i++) { - threads[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); + threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); } for (int i = 0; i < threads.size(); i++) { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index eaccdba9bf..45e060fa4a 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -251,7 +251,7 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int len; encode_variant(p_data, NULL, len); tmpdata.resize(tmpdata.size() + len); - encode_variant(p_data, &tmpdata[pos], len); + encode_variant(p_data, &tmpdata.write[pos], len); return pos; } break; @@ -268,8 +268,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = d.size(); tmpdata.resize(tmpdata.size() + len * 12 + 8); - encode_uint32(TYPE_DICT, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_DICT, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); List<Variant> keys; d.get_key_list(&keys); @@ -288,11 +288,11 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int idx = 0; for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) { - encode_uint32(E->get().hash, &tmpdata[pos + 8 + idx * 12 + 0]); + encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); uint32_t ofs = _pack(E->get().key, tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]); ofs = _pack(d[E->get().key], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 8]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]); idx++; } @@ -306,13 +306,13 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = a.size(); tmpdata.resize(tmpdata.size() + len * 4 + 8); - encode_uint32(TYPE_ARRAY, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_ARRAY, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); for (int i = 0; i < len; i++) { uint32_t ofs = _pack(a[i], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + i * 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + i * 4]); } return pos; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 60e8933751..146b4870e8 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -627,7 +627,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(p_custom_features, &buff[0], len); + err = encode_variant(p_custom_features, buff.ptrw(), len); if (err != OK) { memdelete(file); ERR_FAIL_V(err); @@ -664,7 +664,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, &buff[0], len); + err = encode_variant(value, buff.ptrw(), len); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -959,7 +959,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/name", ""); GLOBAL_DEF("application/run/main_scene", ""); - custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "tscn,scn,res"); + custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); GLOBAL_DEF("application/config/use_custom_user_dir", false); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 9bcc2d4530..859015f44b 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -41,6 +41,7 @@ #include "input_map.h" #include "io/config_file.h" #include "io/http_client.h" +#include "io/image_loader.h" #include "io/marshalls.h" #include "io/multiplayer_api.h" #include "io/networked_multiplayer_peer.h" @@ -53,6 +54,7 @@ #include "io/tcp_server.h" #include "io/translation_loader_po.h" #include "math/a_star.h" +#include "math/expression.h" #include "math/triangle_mesh.h" #include "os/input.h" #include "os/main_loop.h" @@ -60,11 +62,14 @@ #include "path_remap.h" #include "project_settings.h" #include "translation.h" + #include "undo_redo.h" static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; +static ResourceFormatLoaderImage *resource_format_image = NULL; + static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; static _OS *_os = NULL; @@ -111,6 +116,9 @@ void register_core_types() { resource_format_importer = memnew(ResourceFormatImporter); ResourceLoader::add_resource_format_loader(resource_format_importer); + resource_format_image = memnew(ResourceFormatLoaderImage); + ResourceLoader::add_resource_format_loader(resource_format_image); + ClassDB::register_class<Object>(); ClassDB::register_virtual_class<Script>(); @@ -209,6 +217,7 @@ void register_core_singletons() { ClassDB::register_virtual_class<Input>(); ClassDB::register_class<InputMap>(); ClassDB::register_class<_JSON>(); + ClassDB::register_class<Expression>(); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); @@ -237,6 +246,8 @@ void unregister_core_types() { memdelete(_geometry); + if (resource_format_image) + memdelete(resource_format_image); if (resource_saver_binary) memdelete(resource_saver_binary); if (resource_loader_binary) diff --git a/core/ring_buffer.h b/core/ring_buffer.h index de4757612a..00628a4ab3 100644 --- a/core/ring_buffer.h +++ b/core/ring_buffer.h @@ -137,7 +137,7 @@ public: Error write(const T &p_v) { ERR_FAIL_COND_V(space_left() < 1, FAILED); - data[inc(write_pos, 1)] = p_v; + data.write[inc(write_pos, 1)] = p_v; return OK; }; @@ -156,7 +156,7 @@ public: int total = end - pos; for (int i = 0; i < total; i++) { - data[pos + i] = p_buf[src++]; + data.write[pos + i] = p_buf[src++]; }; to_write -= total; pos = 0; @@ -196,7 +196,7 @@ public: data.resize(1 << p_power); if (old_size < new_size && read_pos > write_pos) { for (int i = 0; i < write_pos; i++) { - data[(old_size + i) & mask] = data[i]; + data.write[(old_size + i) & mask] = data[i]; }; write_pos = (old_size + write_pos) & mask; } else { diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index 3b203f6977..692ff722f3 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -57,113 +57,113 @@ return m_val; \ } -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw){ +_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { return InterlockedDecrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { return InterlockedIncrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; } -_ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedAdd((LONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val){ +_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw){ +_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) } -_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { return InterlockedDecrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { return InterlockedIncrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; } -_ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedAdd64((LONGLONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val){ +_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) } // The actual advertised functions; they'll call the right implementation -uint32_t atomic_conditional_increment(register uint32_t *pw) { +uint32_t atomic_conditional_increment(volatile uint32_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint32_t atomic_decrement(register uint32_t *pw) { +uint32_t atomic_decrement(volatile uint32_t *pw) { return _atomic_decrement_impl(pw); } -uint32_t atomic_increment(register uint32_t *pw) { +uint32_t atomic_increment(volatile uint32_t *pw) { return _atomic_increment_impl(pw); } -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_sub_impl(pw, val); } -uint32_t atomic_add(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_add_impl(pw, val); } -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_exchange_if_greater_impl(pw, val); } -uint64_t atomic_conditional_increment(register uint64_t *pw) { +uint64_t atomic_conditional_increment(volatile uint64_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint64_t atomic_decrement(register uint64_t *pw) { +uint64_t atomic_decrement(volatile uint64_t *pw) { return _atomic_decrement_impl(pw); } -uint64_t atomic_increment(register uint64_t *pw) { +uint64_t atomic_increment(volatile uint64_t *pw) { return _atomic_increment_impl(pw); } -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_sub_impl(pw, val); } -uint64_t atomic_add(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_add_impl(pw, val); } -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_exchange_if_greater_impl(pw, val); } #endif diff --git a/core/safe_refcount.h b/core/safe_refcount.h index eff209c2db..36bcf5e576 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -44,7 +44,7 @@ /* Bogus implementation unaware of multiprocessing */ template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { if (*pw == 0) return 0; @@ -55,7 +55,7 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { (*pw)--; @@ -63,7 +63,7 @@ static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { (*pw)++; @@ -71,7 +71,7 @@ static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { (*pw) -= val; @@ -79,7 +79,7 @@ static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { (*pw) += val; @@ -87,7 +87,7 @@ static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { if (val > *pw) *pw = val; @@ -103,7 +103,7 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v // Clang states it supports GCC atomic builtins. template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -115,31 +115,31 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { return __sync_sub_and_fetch(pw, 1); } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { return __sync_add_and_fetch(pw, 1); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { return __sync_sub_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { return __sync_add_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -153,19 +153,19 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v #elif defined(_MSC_VER) // For MSVC use a separate compilation unit to prevent windows.h from polluting // the global namespace. -uint32_t atomic_conditional_increment(register uint32_t *pw); -uint32_t atomic_decrement(register uint32_t *pw); -uint32_t atomic_increment(register uint32_t *pw); -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val); -uint32_t atomic_add(register uint32_t *pw, register uint32_t val); -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val); - -uint64_t atomic_conditional_increment(register uint64_t *pw); -uint64_t atomic_decrement(register uint64_t *pw); -uint64_t atomic_increment(register uint64_t *pw); -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val); -uint64_t atomic_add(register uint64_t *pw, register uint64_t val); -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val); +uint32_t atomic_conditional_increment(volatile uint32_t *pw); +uint32_t atomic_decrement(volatile uint32_t *pw); +uint32_t atomic_increment(volatile uint32_t *pw); +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val); + +uint64_t atomic_conditional_increment(volatile uint64_t *pw); +uint64_t atomic_decrement(volatile uint64_t *pw); +uint64_t atomic_increment(volatile uint64_t *pw); +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val); #else //no threads supported? diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 55d7270473..6949b5802b 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -325,7 +325,7 @@ void ScriptDebuggerLocal::idle_poll() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; @@ -377,7 +377,7 @@ void ScriptDebuggerLocal::profiling_end() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 3955f222f9..c5daaeea47 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -783,13 +783,13 @@ void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { if (p_for_frame) - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs); else - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs); } for (int i = 0; i < ofs; i++) { - profile_info_ptrs[i] = &profile_info[i]; + profile_info_ptrs.write[i] = &profile_info.write[i]; } SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa; @@ -1054,7 +1054,7 @@ void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, co if (idx == -1) { profile_frame_data.push_back(fd); } else { - profile_frame_data[idx] = fd; + profile_frame_data.write[idx] = fd; } } diff --git a/core/script_language.h b/core/script_language.h index 4e81b9b626..71d550d404 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -207,13 +207,20 @@ public: virtual void finish() = 0; /* EDITOR FUNCTIONS */ + struct Warning { + int line; + int code; + String string_code; + String message; + }; + virtual void get_reserved_words(List<String> *p_words) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const = 0; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; diff --git a/core/sort.h b/core/sort.h index a6780309d8..97983829e1 100644 --- a/core/sort.h +++ b/core/sort.h @@ -36,13 +36,25 @@ @author ,,, <red@lunatea> */ +#define ERR_BAD_COMPARE(cond) \ + if (unlikely(cond)) { \ + ERR_PRINT("bad comparison function; sorting will be broken"); \ + break; \ + } + template <class T> struct _DefaultComparator { - inline bool operator()(const T &a, const T &b) const { return (a < b); } + _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; -template <class T, class Comparator = _DefaultComparator<T> > +#ifdef DEBUG_ENABLED +#define SORT_ARRAY_VALIDATE_ENABLED true +#else +#define SORT_ARRAY_VALIDATE_ENABLED false +#endif + +template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { enum { @@ -164,12 +176,23 @@ public: inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { + const int unmodified_first = p_first; + const int unmodified_last = p_last; + while (true) { - while (compare(p_array[p_first], p_pivot)) + while (compare(p_array[p_first], p_pivot)) { + if (Validate) { + ERR_BAD_COMPARE(p_first == unmodified_last - 1) + } p_first++; + } p_last--; - while (compare(p_pivot, p_array[p_last])) + while (compare(p_pivot, p_array[p_last])) { + if (Validate) { + ERR_BAD_COMPARE(p_last == unmodified_first) + } p_last--; + } if (!(p_first < p_last)) return p_first; @@ -238,6 +261,9 @@ public: int next = p_last - 1; while (compare(p_value, p_array[next])) { + if (Validate) { + ERR_BAD_COMPARE(next == 0) + } p_array[p_last] = p_array[next]; p_last = next; next--; diff --git a/core/string_buffer.h b/core/string_buffer.h index 7e9b151bea..5d3be0ecf1 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -42,7 +42,7 @@ class StringBuffer { int string_length; _FORCE_INLINE_ CharType *current_buffer_ptr() { - return static_cast<Vector<CharType> &>(buffer).empty() ? short_buffer : buffer.ptrw(); + return static_cast<String &>(buffer).empty() ? short_buffer : buffer.ptrw(); } public: diff --git a/core/translation.cpp b/core/translation.cpp index aaa4de5912..78115c3749 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -1171,13 +1171,11 @@ void TranslationServer::_bind_methods() { void TranslationServer::load_translations() { String locale = get_locale(); - bool found = _load_translations("locale/translations"); //all + _load_translations("locale/translations"); //all + _load_translations("locale/translations_" + locale.substr(0, 2)); - if (_load_translations("locale/translations_" + locale.substr(0, 2))) - found = true; if (locale.substr(0, 2) != locale) { - if (_load_translations("locale/translations_" + locale)) - found = true; + _load_translations("locale/translations_" + locale); } } diff --git a/core/typedefs.h b/core/typedefs.h index 71771ea4e6..57afa3bdb4 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -58,12 +58,8 @@ #endif #ifndef _FORCE_INLINE_ -#ifdef DEBUG_ENABLED -#define _FORCE_INLINE_ inline -#else #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif -#endif //custom, gcc-safe offsetof, because gcc complains a lot. template <class T> @@ -267,7 +263,7 @@ static inline uint64_t BSWAP64(uint64_t x) { template <class T> struct Comparator { - inline bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } + _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } }; void _global_lock(); diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index b9a2fdd0ac..3d90608dd7 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -39,7 +39,7 @@ void UndoRedo::_discard_redo() { for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions[i].do_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -70,7 +70,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { if (p_mode == MERGE_ENDS) { // Clear all do ops from last action, and delete all object references - List<Operation>::Element *E = actions[current_action + 1].do_ops.front(); + List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); while (E) { @@ -83,11 +83,11 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } E = E->next(); - actions[current_action + 1].do_ops.pop_front(); + actions.write[current_action + 1].do_ops.pop_front(); } } - actions[actions.size() - 1].last_tick = ticks; + actions.write[actions.size() - 1].last_tick = ticks; merge_mode = p_mode; @@ -122,7 +122,7 @@ void UndoRedo::add_do_method(Object *p_object, const String &p_method, VARIANT_A for (int i = 0; i < VARIANT_ARG_MAX; i++) { do_op.args[i] = *argptr[i]; } - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT_ARG_DECLARE) { @@ -147,7 +147,7 @@ void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT for (int i = 0; i < VARIANT_ARG_MAX; i++) { undo_op.args[i] = *argptr[i]; } - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -162,7 +162,7 @@ void UndoRedo::add_do_property(Object *p_object, const String &p_property, const do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; do_op.args[0] = p_value; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -182,7 +182,7 @@ void UndoRedo::add_undo_property(Object *p_object, const String &p_property, con undo_op.type = Operation::TYPE_PROPERTY; undo_op.name = p_property; undo_op.args[0] = p_value; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_reference(Object *p_object) { @@ -195,7 +195,7 @@ void UndoRedo::add_do_reference(Object *p_object) { do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); do_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_reference(Object *p_object) { @@ -213,7 +213,7 @@ void UndoRedo::add_undo_reference(Object *p_object) { undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); undo_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::_pop_history_tail() { @@ -223,7 +223,7 @@ void UndoRedo::_pop_history_tail() { if (!actions.size()) return; - for (List<Operation>::Element *E = actions[0].undo_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -307,7 +307,7 @@ bool UndoRedo::redo() { return false; //nothing to redo current_action++; - _process_operation_list(actions[current_action].do_ops.front()); + _process_operation_list(actions.write[current_action].do_ops.front()); version++; return true; @@ -318,7 +318,7 @@ bool UndoRedo::undo() { ERR_FAIL_COND_V(action_level > 0, false); if (current_action < 0) return false; //nothing to redo - _process_operation_list(actions[current_action].undo_ops.front()); + _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; diff --git a/core/ustring.cpp b/core/ustring.cpp index bee5f5ffdb..96d142d85b 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -102,6 +102,15 @@ bool CharString::operator<(const CharString &p_right) const { return is_str_less(get_data(), p_right.get_data()); } +CharString &CharString::operator+=(char p_char) { + + resize(size() ? size() + 1 : 2); + set(length(), 0); + set(length() - 1, p_char); + + return *this; +} + const char *CharString::get_data() const { if (size()) @@ -161,14 +170,21 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { return; } - resize(len + 1); - set(len, 0); + copy_from_unchecked(p_cstr, len); +} - CharType *dst = &operator[](0); +// assumes the following have already been validated: +// p_char != NULL +// p_length > 0 +// p_length <= p_char strlen +void String::copy_from_unchecked(const CharType *p_char, int p_length) { + resize(p_length + 1); + set(p_length, 0); - for (int i = 0; i < len; i++) { + CharType *dst = &operator[](0); - dst[i] = p_cstr[i]; + for (int i = 0; i < p_length; i++) { + dst[i] = p_char[i]; } } @@ -921,8 +937,8 @@ String String::to_upper() const { for (int i = 0; i < upper.size(); i++) { - const char s = upper[i]; - const char t = _find_upper(s); + const CharType s = upper[i]; + const CharType t = _find_upper(s); if (s != t) // avoid copy on write upper[i] = t; } @@ -936,8 +952,8 @@ String String::to_lower() const { for (int i = 0; i < lower.size(); i++) { - const char s = lower[i]; - const char t = _find_lower(s); + const CharType s = lower[i]; + const CharType t = _find_lower(s); if (s != t) // avoid copy on write lower[i] = t; } @@ -1571,6 +1587,7 @@ String::String(const char *p_str) { copy_from(p_str); } + String::String(const CharType *p_str, int p_clip_to_len) { copy_from(p_str, p_clip_to_len); @@ -2190,7 +2207,7 @@ Vector<uint8_t> String::md5_buffer() const { Vector<uint8_t> ret; ret.resize(16); for (int i = 0; i < 16; i++) { - ret[i] = ctx.digest[i]; + ret.write[i] = ctx.digest[i]; }; return ret; @@ -2207,7 +2224,7 @@ Vector<uint8_t> String::sha256_buffer() const { Vector<uint8_t> ret; ret.resize(32); for (int i = 0; i < 32; i++) { - ret[i] = hash[i]; + ret.write[i] = hash[i]; } return ret; @@ -2246,7 +2263,9 @@ String String::substr(int p_from, int p_chars) const { return String(*this); } - return String(&c_str()[p_from], p_chars); + String s = String(); + s.copy_from_unchecked(&c_str()[p_from], p_chars); + return s; } int String::find_last(const String &p_str) const { @@ -2664,7 +2683,7 @@ Vector<String> String::bigrams() const { } b.resize(n_pairs); for (int i = 0; i < n_pairs; i++) { - b[i] = substr(i, 2); + b.write[i] = substr(i, 2); } return b; } @@ -2759,7 +2778,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } else { ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); } @@ -2772,7 +2791,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", i_as_str), val); + new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } } } else if (values.get_type() == Variant::DICTIONARY) { @@ -2792,7 +2811,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } } else { ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); @@ -3023,14 +3042,14 @@ String String::strip_escapes() const { return substr(beg, end - beg); } -String String::lstrip(const Vector<CharType> &p_chars) const { +String String::lstrip(const String &p_chars) const { int len = length(); int beg; for (beg = 0; beg < len; beg++) { - if (p_chars.find(operator[](beg)) == -1) + if (p_chars.find(&ptr()[beg]) == -1) break; } @@ -3040,14 +3059,14 @@ String String::lstrip(const Vector<CharType> &p_chars) const { return substr(beg, len - beg); } -String String::rstrip(const Vector<CharType> &p_chars) const { +String String::rstrip(const String &p_chars) const { int len = length(); int end; for (end = len - 1; end >= 0; end--) { - if (p_chars.find(operator[](end)) == -1) + if (p_chars.find(&ptr()[end]) == -1) break; } @@ -3859,11 +3878,9 @@ String String::percent_decode() const { c += d; i += 2; } - pe.push_back(c); + pe += c; } - pe.push_back(0); - return String::utf8(pe.ptr()); } diff --git a/core/ustring.h b/core/ustring.h index b57e9629d9..3b4405833c 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -32,6 +32,7 @@ #define RSTRING_H #include "array.h" +#include "cowdata.h" #include "typedefs.h" #include "vector.h" @@ -39,9 +40,27 @@ @author red <red@killy> */ -class CharString : public Vector<char> { +class CharString { + + CowData<char> _cowdata; + public: + _FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + + _FORCE_INLINE_ char get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const char get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ char &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const char &operator[](int p_index) const { return _cowdata.get(p_index); } + + _FORCE_INLINE_ CharString() {} + _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + bool operator<(const CharString &p_right) const; + CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; operator const char *() { return get_data(); }; @@ -60,11 +79,14 @@ struct StrRange { } }; -class String : public Vector<CharType> { +class String { + + CowData<CharType> _cowdata; void copy_from(const char *p_cstr); void copy_from(const CharType *p_cstr, int p_clip_to = -1); void copy_from(const CharType &p_char); + void copy_from_unchecked(const CharType *p_char, int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; public: @@ -73,6 +95,21 @@ public: npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string) }; + _FORCE_INLINE_ CharType *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const CharType *ptr() const { return _cowdata.ptr(); } + + void remove(int p_index) { _cowdata.remove(p_index); } + + _FORCE_INLINE_ void clear() { resize(0); } + + _FORCE_INLINE_ CharType get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const CharType get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + _FORCE_INLINE_ CharType &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const CharType &operator[](int p_index) const { return _cowdata.get(p_index); } + bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; @@ -191,8 +228,8 @@ public: String dedent() const; String strip_edges(bool left = true, bool right = true) const; String strip_escapes() const; - String lstrip(const Vector<CharType> &p_chars) const; - String rstrip(const Vector<CharType> &p_chars) const; + String lstrip(const String &p_chars) const; + String rstrip(const String &p_chars) const; String get_extension() const; String get_basename() const; String plus_file(const String &p_file) const; @@ -253,9 +290,10 @@ public: * The constructors must not depend on other overloads */ /* String(CharType p_char);*/ - inline String() {} - inline String(const String &p_str) : - Vector(p_str) {} + + _FORCE_INLINE_ String() {} + _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } + String(const char *p_str); String(const CharType *p_str, int p_clip_to_len = -1); String(const StrRange &p_range); diff --git a/core/variant.cpp b/core/variant.cpp index c48aa57652..e4be5520bc 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -1878,7 +1878,7 @@ Variant::operator Vector<RID>() const { Vector<RID> rids; rids.resize(va.size()); for (int i = 0; i < rids.size(); i++) - rids[i] = va[i]; + rids.write[i] = va[i]; return rids; } @@ -1891,7 +1891,7 @@ Variant::operator Vector<Vector2>() const { return Vector<Vector2>(); to.resize(len); PoolVector<Vector2>::Read r = from.read(); - Vector2 *w = &to[0]; + Vector2 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -1945,7 +1945,7 @@ Variant::operator Vector<Plane>() const { planes.resize(va_size); for (int i = 0; i < va_size; i++) - planes[i] = va[i]; + planes.write[i] = va[i]; return planes; } @@ -1958,7 +1958,7 @@ Variant::operator Vector<Variant>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1971,7 +1971,7 @@ Variant::operator Vector<uint8_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1983,7 +1983,7 @@ Variant::operator Vector<int>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1995,7 +1995,7 @@ Variant::operator Vector<real_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2008,7 +2008,7 @@ Variant::operator Vector<String>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2020,7 +2020,7 @@ Variant::operator Vector<StringName>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2034,7 +2034,7 @@ Variant::operator Vector<Vector3>() const { return Vector<Vector3>(); to.resize(len); PoolVector<Vector3>::Read r = from.read(); - Vector3 *w = &to[0]; + Vector3 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -2050,7 +2050,7 @@ Variant::operator Vector<Color>() const { return Vector<Color>(); to.resize(len); PoolVector<Color>::Read r = from.read(); - Color *w = &to[0]; + Color *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; diff --git a/core/variant.h b/core/variant.h index 4b245d25e6..b48a0b3e73 100644 --- a/core/variant.h +++ b/core/variant.h @@ -398,9 +398,9 @@ public: void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); - static void get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); - static bool has_numeric_constant(Variant::Type p_type, const StringName &p_value); - static int get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); + static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); + static bool has_constant(Variant::Type p_type, const StringName &p_value); + static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index e6f36ecbf1..19308ff683 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -30,6 +30,7 @@ #include "variant.h" +#include "core/color_names.inc" #include "core_string_names.h" #include "io/compression.h" #include "object.h" @@ -64,7 +65,7 @@ struct _VariantCall { if (arg_count == 0) return true; - Variant::Type *tptr = &arg_types[0]; + const Variant::Type *tptr = &arg_types[0]; for (int i = 0; i < arg_count; i++) { @@ -446,8 +447,12 @@ struct _VariantCall { VCALL_LOCALMEM1(Quat, set_euler); VCALL_LOCALMEM2(Quat, set_axis_angle); - VCALL_LOCALMEM0R(Color, to_rgba32); VCALL_LOCALMEM0R(Color, to_argb32); + VCALL_LOCALMEM0R(Color, to_abgr32); + VCALL_LOCALMEM0R(Color, to_rgba32); + VCALL_LOCALMEM0R(Color, to_argb64); + VCALL_LOCALMEM0R(Color, to_abgr64); + VCALL_LOCALMEM0R(Color, to_rgba64); VCALL_LOCALMEM0R(Color, gray); VCALL_LOCALMEM0R(Color, inverted); VCALL_LOCALMEM0R(Color, contrasted); @@ -987,6 +992,7 @@ struct _VariantCall { #ifdef DEBUG_ENABLED List<StringName> value_ordered; #endif + Map<StringName, Variant> variant_value; }; static ConstantData *constant_data; @@ -998,6 +1004,11 @@ struct _VariantCall { constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } + + static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + + constant_data[p_type].variant_value[p_constant_name] = p_constant_value; + } }; _VariantCall::TypeFunc *_VariantCall::type_funcs = NULL; @@ -1350,7 +1361,7 @@ void Variant::get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_lis } } -void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { +void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); @@ -1366,16 +1377,21 @@ void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringNa p_constants->push_back(E->key()); #endif } + + for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { + + p_constants->push_back(E->key()); + } } -bool Variant::has_numeric_constant(Variant::Type p_type, const StringName &p_value) { +bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - return cd.value.has(p_value); + return cd.value.has(p_value) || cd.variant_value.has(p_value); } -int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { +Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { if (r_valid) *r_valid = false; @@ -1385,7 +1401,14 @@ int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName & Map<StringName, int>::Element *E = cd.value.find(p_value); if (!E) { - return -1; + Map<StringName, Variant>::Element *E = cd.variant_value.find(p_value); + if (E) { + if (r_valid) + *r_valid = true; + return E->get(); + } else { + return -1; + } } if (r_valid) *r_valid = true; @@ -1613,8 +1636,12 @@ void register_variant_methods() { ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray()); ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", REAL, "angle", varray()); - ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); ADDFUNC0R(COLOR, INT, Color, to_argb32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_argb64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba64, varray()); ADDFUNC0R(COLOR, REAL, Color, gray, varray()); ADDFUNC0R(COLOR, COLOR, Color, inverted, varray()); ADDFUNC0R(COLOR, COLOR, Color, contrasted, varray()); @@ -1850,9 +1877,62 @@ void register_variant_methods() { /* REGISTER CONSTANTS */ + _populate_named_colors(); + for (Map<String, Color>::Element *color = _named_colors.front(); color; color = color->next()) { + _VariantCall::add_variant_constant(Variant::COLOR, color->key(), color->value()); + } + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); + + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D(1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); + + Transform identity_transform, transform_x, transform_y, transform_z; + identity_transform.set(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); + transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); + transform_x.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); + transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); + + _VariantCall::add_variant_constant(Variant::PLANE, "X", Plane(Vector3(1, 0, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Y", Plane(Vector3(0, 1, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Z", Plane(Vector3(0, 0, 1), 0)); + + _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); + + CharType black_circle[2] = { 0x25CF, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_CIRCLE", String(black_circle)); + CharType white_circle[2] = { 0x25CB, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_CIRCLE", String(white_circle)); + CharType black_diamond[2] = { 0x25C6, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_DIAMOND", String(black_diamond)); + CharType white_diamond[2] = { 0x25C7, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_DIAMOND", String(white_diamond)); + + _VariantCall::add_variant_constant(Variant::NODE_PATH, "CURRENT", String(".")); + _VariantCall::add_variant_constant(Variant::NODE_PATH, "PARENT", String("..")); } void unregister_variant_methods() { diff --git a/core/vector.h b/core/vector.h index f586471e27..52e8758f9b 100644 --- a/core/vector.h +++ b/core/vector.h @@ -36,129 +36,68 @@ * @author Juan Linietsky * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. */ +#include "cowdata.h" #include "error_macros.h" #include "os/memory.h" -#include "safe_refcount.h" #include "sort.h" template <class T> -class Vector { - - mutable T *_ptr; - - // internal helpers +class VectorWriteProxy { + friend class Vector<T>; + CowData<T> *_parent; - _FORCE_INLINE_ uint32_t *_get_refcount() const { + _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) : + _parent(parent){}; - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 2; - } - - _FORCE_INLINE_ uint32_t *_get_size() const { - - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 1; - } - _FORCE_INLINE_ T *_get_data() const { - - if (!_ptr) - return NULL; - return reinterpret_cast<T *>(_ptr); - } - - _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { - //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); - return next_power_of_2(p_elements * sizeof(T)); - } +public: + _FORCE_INLINE_ T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, _parent->size()); - _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { -#if defined(_add_overflow) && defined(_mul_overflow) - size_t o; - size_t p; - if (_mul_overflow(p_elements, sizeof(T), &o)) return false; - *out = next_power_of_2(o); - if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here - return true; -#else - // Speed is more important than correctness here, do the operations unchecked - // and hope the best - *out = _get_alloc_size(p_elements); - return true; -#endif + return _parent->ptrw()[p_index]; } +}; - void _unref(void *p_data); +template <class T> +class Vector { + friend class VectorWriteProxy<T>; - void _copy_from(const Vector &p_from); - void _copy_on_write(); + CowData<T> *_cowdata; public: - _FORCE_INLINE_ T *ptrw() { - if (!_ptr) return NULL; - _copy_on_write(); - return (T *)_get_data(); - } - _FORCE_INLINE_ const T *ptr() const { - if (!_ptr) return NULL; - return _get_data(); - } - - _FORCE_INLINE_ void clear() { resize(0); } + VectorWriteProxy<T> write; - _FORCE_INLINE_ int size() const { - uint32_t *size = (uint32_t *)_get_size(); - if (size) - return *size; - else - return 0; - } - _FORCE_INLINE_ bool empty() const { return _ptr == 0; } - Error resize(int p_size); bool push_back(const T &p_elem); - void remove(int p_index); + void remove(int p_index) { _cowdata->remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) remove(idx); }; void invert(); - template <class T_val> - int find(const T_val &p_val, int p_from = 0) const; - - void set(int p_index, const T &p_elem); - T get(int p_index) const; - - inline T &operator[](int p_index) { - - CRASH_BAD_INDEX(p_index, size()); - - _copy_on_write(); // wants to write, so copy on write. - - return _get_data()[p_index]; - } - - inline const T &operator[](int p_index) const { - - CRASH_BAD_INDEX(p_index, size()); + _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); } + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); } - // no cow needed, since it's reading - return _get_data()[p_index]; - } + _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); } + _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata->size(); } + Error resize(int p_size) { return _cowdata->resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); } + Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); } - Error insert(int p_pos, const T &p_val); + void append_array(const Vector<T> &p_other); template <class C> void sort_custom() { - int len = size(); + int len = _cowdata->size(); if (len == 0) return; - T *data = &operator[](0); + + T *data = ptrw(); SortArray<T, C> sorter; sorter.sort(data, len); } @@ -170,7 +109,7 @@ public: void ordered_insert(const T &p_val) { int i; - for (i = 0; i < size(); i++) { + for (i = 0; i < _cowdata->size(); i++) { if (p_val < operator[](i)) { break; @@ -179,173 +118,56 @@ public: insert(i, p_val); } - void operator=(const Vector &p_from); - Vector(const Vector &p_from); + int find(const T &p_val, int p_from = 0) const { + int ret = -1; + if (p_from < 0 || size() == 0) + return ret; - _FORCE_INLINE_ Vector(); - _FORCE_INLINE_ ~Vector(); -}; - -template <class T> -void Vector<T>::_unref(void *p_data) { - - if (!p_data) - return; - - uint32_t *refc = _get_refcount(); - - if (atomic_decrement(refc) > 0) - return; // still in use - // clean up - - uint32_t *count = _get_size(); - T *data = (T *)(count + 1); - - for (uint32_t i = 0; i < *count; i++) { - // call destructors - data[i].~T(); - } - - // free mem - Memory::free_static((uint8_t *)p_data, true); -} - -template <class T> -void Vector<T>::_copy_on_write() { - - if (!_ptr) - return; - - uint32_t *refc = _get_refcount(); + for (int i = p_from; i < size(); i++) { - if (*refc > 1) { - /* in use by more than me */ - uint32_t current_size = *_get_size(); - - uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); - - *(mem_new - 2) = 1; //refcount - *(mem_new - 1) = current_size; //size - - T *_data = (T *)(mem_new); - - // initialize new elements - for (uint32_t i = 0; i < current_size; i++) { - - memnew_placement(&_data[i], T(_get_data()[i])); - } - - _unref(_ptr); - _ptr = _data; - } -} - -template <class T> -template <class T_val> -int Vector<T>::find(const T_val &p_val, int p_from) const { - - int ret = -1; - if (p_from < 0 || size() == 0) - return ret; - - for (int i = p_from; i < size(); i++) { - - if (operator[](i) == p_val) { - ret = i; - break; + if (ptr()[i] == p_val) { + ret = i; + break; + }; }; - }; - return ret; -} - -template <class T> -Error Vector<T>::resize(int p_size) { - - ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); - - if (p_size == size()) - return OK; - - if (p_size == 0) { - // wants to clean up - _unref(_ptr); - _ptr = NULL; - return OK; + return ret; } - // possibly changing size, copy on write - _copy_on_write(); - - size_t alloc_size; - ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); - - if (p_size > size()) { - - if (size() == 0) { - // alloc from scratch - uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); - ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); - *(ptr - 1) = 0; //size, currently none - *(ptr - 2) = 1; //refcount - - _ptr = (T *)ptr; - - } else { - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - _ptr = (T *)(_ptrnew); - } - - // construct the newly created elements - T *elems = _get_data(); - - for (int i = *_get_size(); i < p_size; i++) { - - memnew_placement(&elems[i], T); - } - - *_get_size() = p_size; - - } else if (p_size < size()) { - - // deinitialize no longer needed elements - for (uint32_t i = p_size; i < *_get_size(); i++) { - - T *t = &_get_data()[i]; - t->~T(); - } - - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - - _ptr = (T *)(_ptrnew); - - *_get_size() = p_size; + _FORCE_INLINE_ Vector() : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) {} + _FORCE_INLINE_ Vector(const Vector &p_from) : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); } + inline Vector &operator=(const Vector &p_from) { + _cowdata->_ref(p_from._cowdata); + return *this; } - return OK; -} + _FORCE_INLINE_ ~Vector() { + delete _cowdata; + } +}; template <class T> void Vector<T>::invert() { for (int i = 0; i < size() / 2; i++) { - - SWAP(operator[](i), operator[](size() - i - 1)); + T *p = ptrw(); + SWAP(p[i], p[size() - i - 1]); } } template <class T> -void Vector<T>::set(int p_index, const T &p_elem) { - - operator[](p_index) = p_elem; -} - -template <class T> -T Vector<T>::get(int p_index) const { - - return operator[](p_index); +void Vector<T>::append_array(const Vector<T> &p_other) { + const int ds = p_other.size(); + if (ds == 0) + return; + const int bs = size(); + resize(bs + ds); + for (int i = 0; i < ds; ++i) + ptrw()[bs + i] = p_other[i]; } template <class T> @@ -358,72 +180,4 @@ bool Vector<T>::push_back(const T &p_elem) { return false; } -template <class T> -void Vector<T>::remove(int p_index) { - - ERR_FAIL_INDEX(p_index, size()); - T *p = ptrw(); - int len = size(); - for (int i = p_index; i < len - 1; i++) { - - p[i] = p[i + 1]; - }; - - resize(len - 1); -}; - -template <class T> -void Vector<T>::_copy_from(const Vector &p_from) { - - if (_ptr == p_from._ptr) - return; // self assign, do nothing. - - _unref(_ptr); - _ptr = NULL; - - if (!p_from._ptr) - return; //nothing to do - - if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference - _ptr = p_from._ptr; - } -} - -template <class T> -void Vector<T>::operator=(const Vector &p_from) { - - _copy_from(p_from); -} - -template <class T> -Error Vector<T>::insert(int p_pos, const T &p_val) { - - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); - resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) - set(i, get(i - 1)); - set(p_pos, p_val); - - return OK; -} - -template <class T> -Vector<T>::Vector(const Vector &p_from) { - - _ptr = NULL; - _copy_from(p_from); -} - -template <class T> -Vector<T>::Vector() { - - _ptr = NULL; -} - -template <class T> -Vector<T>::~Vector() { - - _unref(_ptr); -} - #endif diff --git a/core/vmap.h b/core/vmap.h index 8636c02997..ce0ddc4ec6 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -31,8 +31,8 @@ #ifndef VMAP_H #define VMAP_H +#include "cowdata.h" #include "typedefs.h" -#include "vector.h" template <class T, class V> class VMap { @@ -51,17 +51,17 @@ class VMap { } }; - Vector<_Pair> _data; + CowData<_Pair> _cowdata; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { r_exact = false; - if (_data.empty()) + if (_cowdata.empty()) return 0; int low = 0; - int high = _data.size() - 1; - const _Pair *a = &_data[0]; + int high = _cowdata.size() - 1; + const _Pair *a = _cowdata.ptr(); int middle = 0; #if DEBUG_ENABLED @@ -89,13 +89,13 @@ class VMap { _FORCE_INLINE_ int _find_exact(const T &p_val) const { - if (_data.empty()) + if (_cowdata.empty()) return -1; int low = 0; - int high = _data.size() - 1; + int high = _cowdata.size() - 1; int middle; - const _Pair *a = &_data[0]; + const _Pair *a = _cowdata.ptr(); while (low <= high) { middle = (low + high) / 2; @@ -118,10 +118,10 @@ public: bool exact; int pos = _find(p_key, exact); if (exact) { - _data[pos].value = p_val; + _cowdata.get_m(pos).value = p_val; return pos; } - _data.insert(pos, _Pair(p_key, p_val)); + _cowdata.insert(pos, _Pair(p_key, p_val)); return pos; } @@ -135,7 +135,7 @@ public: int pos = _find_exact(p_val); if (pos < 0) return; - _data.remove(pos); + _cowdata.remove(pos); } int find(const T &p_val) const { @@ -149,37 +149,37 @@ public: return _find(p_val, exact); } - _FORCE_INLINE_ int size() const { return _data.size(); } - _FORCE_INLINE_ bool empty() const { return _data.empty(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } const _Pair *get_array() const { - return _data.ptr(); + return _cowdata.ptr(); } _Pair *get_array() { - return _data.ptr(); + return _cowdata.ptrw(); } const V &getv(int p_index) const { - return _data[p_index].value; + return _cowdata.get(p_index).value; } V &getv(int p_index) { - return _data[p_index].value; + return _cowdata.get_m(p_index).value; } const T &getk(int p_index) const { - return _data[p_index].key; + return _cowdata.get(p_index).key; } T &getk(int p_index) { - return _data[p_index].key; + return _cowdata.get_m(p_index).key; } inline const V &operator[](const T &p_key) const { @@ -188,7 +188,7 @@ public: CRASH_COND(pos < 0); - return _data[pos].value; + return _cowdata.get(pos).value; } inline V &operator[](const T &p_key) { @@ -199,7 +199,14 @@ public: pos = insert(p_key, val); } - return _data[pos].value; + return _cowdata.get_m(pos).value; + } + + _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; } }; #endif // VMAP_H diff --git a/core/vset.h b/core/vset.h index 449943b4a1..7f4d8e7f62 100644 --- a/core/vset.h +++ b/core/vset.h @@ -133,7 +133,7 @@ public: inline T &operator[](int p_index) { - return _data[p_index]; + return _data.write[p_index]; } inline const T &operator[](int p_index) const { diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index b5f5fed3f9..2cfdfafea1 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -202,7 +202,7 @@ </description> </method> <method name="convert"> - <return type="Object"> + <return type="Variant"> </return> <argument index="0" name="what" type="Variant"> </argument> @@ -394,7 +394,7 @@ </description> </method> <method name="funcref"> - <return type="Object"> + <return type="FuncRef"> </return> <argument index="0" name="instance" type="Object"> </argument> @@ -411,6 +411,12 @@ [/codeblock] </description> </method> + <method name="get_stack"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="hash"> <return type="int"> </return> @@ -508,7 +514,8 @@ <argument index="0" name="var" type="Variant"> </argument> <description> - Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc. Note: Generates a fatal error if Variant can not provide a length. + Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc. + [b]Note:[/b] Generates a fatal error if Variant can not provide a length. [codeblock] a = [1, 2, 3, 4] len(a) # returns 4 @@ -541,12 +548,13 @@ </description> </method> <method name="load"> - <return type="Object"> + <return type="Resource"> </return> <argument index="0" name="path" type="String"> </argument> <description> - Loads a resource from the filesystem located at [code]path[/code]. Note: resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". + Loads a resource from the filesystem located at [code]path[/code]. + [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". [codeblock] # load a scene called main located in the root of the project directory var main = load("res://main.tscn") @@ -559,7 +567,8 @@ <argument index="0" name="s" type="float"> </argument> <description> - Natural logarithm. The amount of time needed to reach a certain level of continuous growth. Note: This is not the same as the log function on your calculator which is a base 10 logarithm. + Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + [b]Note:[/b] This is not the same as the log function on your calculator which is a base 10 logarithm. [codeblock] log(10) # returns 2.302585 [/codeblock] @@ -658,7 +667,8 @@ <argument index="0" name="path" type="String"> </argument> <description> - Returns a resource from the filesystem that is loaded during script parsing. Note: resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". + Returns a resource from the filesystem that is loaded during script parsing. + [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path". [codeblock] # load a scene called main located in the root of the project directory var main = preload("res://main.tscn") @@ -676,6 +686,12 @@ [/codeblock] </description> </method> + <method name="print_debug" qualifiers="vararg"> + <return type="void"> + </return> + <description> + </description> + </method> <method name="print_stack"> <return type="void"> </return> @@ -1076,7 +1092,7 @@ </description> </method> <method name="weakref"> - <return type="Object"> + <return type="WeakRef"> </return> <argument index="0" name="obj" type="Object"> </argument> diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 7f94676e93..47553659c9 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -883,6 +883,10 @@ <constant name="BUTTON_MIDDLE" value="3" enum="ButtonList"> Middle Mouse Button </constant> + <constant name="BUTTON_XBUTTON1" value="8" enum="ButtonList"> + </constant> + <constant name="BUTTON_XBUTTON2" value="9" enum="ButtonList"> + </constant> <constant name="BUTTON_WHEEL_UP" value="4" enum="ButtonList"> Mouse wheel up </constant> @@ -904,6 +908,10 @@ <constant name="BUTTON_MASK_MIDDLE" value="4" enum="ButtonList"> Middle Mouse Button Mask </constant> + <constant name="BUTTON_MASK_XBUTTON1" value="128" enum="ButtonList"> + </constant> + <constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList"> + </constant> <constant name="JOY_BUTTON_0" value="0" enum="JoystickList"> Joypad Button 0 </constant> @@ -1073,6 +1081,20 @@ <constant name="JOY_ANALOG_R2" value="7" enum="JoystickList"> Joypad Right Analog Trigger </constant> + <constant name="MIDI_MESSAGE_NOTE_OFF" value="8" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_NOTE_ON" value="9" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_AFTERTOUCH" value="10" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_CONTROL_CHANGE" value="11" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_PROGRAM_CHANGE" value="12" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_CHANNEL_PRESSURE" value="13" enum="MidiMessageList"> + </constant> + <constant name="MIDI_MESSAGE_PITCH_BEND" value="14" enum="MidiMessageList"> + </constant> <constant name="OK" value="0" enum="Error"> Functions that return Error return OK when no error occurred. Most functions don't return errors and/or just print errors to STDOUT. </constant> diff --git a/doc/classes/AnimatedTexture.xml b/doc/classes/AnimatedTexture.xml new file mode 100644 index 0000000000..f874c43ef1 --- /dev/null +++ b/doc/classes/AnimatedTexture.xml @@ -0,0 +1,1045 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimatedTexture" inherits="Texture" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="fps" type="float" setter="set_fps" getter="get_fps"> + </member> + <member name="frame_0/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_0/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_1/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_1/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_10/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_10/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_100/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_100/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_101/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_101/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_102/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_102/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_103/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_103/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_104/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_104/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_105/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_105/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_106/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_106/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_107/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_107/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_108/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_108/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_109/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_109/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_11/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_11/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_110/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_110/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_111/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_111/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_112/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_112/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_113/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_113/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_114/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_114/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_115/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_115/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_116/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_116/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_117/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_117/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_118/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_118/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_119/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_119/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_12/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_12/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_120/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_120/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_121/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_121/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_122/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_122/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_123/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_123/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_124/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_124/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_125/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_125/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_126/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_126/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_127/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_127/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_128/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_128/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_129/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_129/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_13/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_13/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_130/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_130/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_131/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_131/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_132/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_132/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_133/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_133/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_134/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_134/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_135/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_135/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_136/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_136/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_137/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_137/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_138/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_138/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_139/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_139/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_14/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_14/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_140/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_140/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_141/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_141/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_142/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_142/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_143/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_143/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_144/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_144/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_145/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_145/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_146/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_146/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_147/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_147/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_148/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_148/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_149/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_149/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_15/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_15/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_150/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_150/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_151/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_151/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_152/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_152/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_153/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_153/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_154/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_154/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_155/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_155/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_156/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_156/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_157/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_157/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_158/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_158/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_159/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_159/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_16/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_16/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_160/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_160/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_161/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_161/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_162/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_162/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_163/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_163/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_164/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_164/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_165/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_165/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_166/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_166/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_167/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_167/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_168/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_168/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_169/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_169/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_17/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_17/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_170/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_170/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_171/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_171/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_172/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_172/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_173/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_173/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_174/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_174/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_175/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_175/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_176/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_176/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_177/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_177/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_178/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_178/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_179/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_179/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_18/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_18/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_180/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_180/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_181/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_181/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_182/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_182/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_183/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_183/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_184/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_184/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_185/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_185/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_186/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_186/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_187/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_187/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_188/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_188/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_189/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_189/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_19/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_19/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_190/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_190/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_191/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_191/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_192/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_192/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_193/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_193/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_194/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_194/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_195/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_195/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_196/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_196/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_197/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_197/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_198/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_198/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_199/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_199/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_2/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_2/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_20/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_20/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_200/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_200/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_201/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_201/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_202/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_202/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_203/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_203/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_204/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_204/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_205/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_205/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_206/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_206/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_207/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_207/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_208/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_208/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_209/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_209/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_21/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_21/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_210/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_210/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_211/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_211/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_212/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_212/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_213/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_213/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_214/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_214/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_215/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_215/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_216/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_216/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_217/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_217/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_218/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_218/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_219/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_219/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_22/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_22/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_220/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_220/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_221/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_221/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_222/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_222/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_223/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_223/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_224/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_224/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_225/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_225/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_226/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_226/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_227/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_227/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_228/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_228/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_229/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_229/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_23/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_23/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_230/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_230/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_231/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_231/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_232/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_232/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_233/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_233/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_234/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_234/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_235/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_235/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_236/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_236/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_237/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_237/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_238/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_238/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_239/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_239/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_24/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_24/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_240/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_240/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_241/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_241/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_242/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_242/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_243/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_243/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_244/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_244/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_245/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_245/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_246/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_246/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_247/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_247/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_248/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_248/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_249/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_249/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_25/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_25/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_250/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_250/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_251/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_251/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_252/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_252/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_253/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_253/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_254/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_254/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_255/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_255/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_26/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_26/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_27/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_27/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_28/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_28/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_29/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_29/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_3/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_3/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_30/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_30/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_31/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_31/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_32/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_32/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_33/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_33/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_34/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_34/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_35/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_35/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_36/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_36/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_37/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_37/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_38/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_38/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_39/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_39/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_4/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_4/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_40/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_40/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_41/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_41/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_42/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_42/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_43/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_43/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_44/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_44/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_45/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_45/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_46/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_46/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_47/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_47/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_48/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_48/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_49/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_49/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_5/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_5/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_50/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_50/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_51/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_51/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_52/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_52/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_53/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_53/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_54/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_54/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_55/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_55/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_56/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_56/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_57/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_57/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_58/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_58/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_59/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_59/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_6/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_6/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_60/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_60/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_61/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_61/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_62/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_62/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_63/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_63/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_64/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_64/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_65/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_65/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_66/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_66/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_67/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_67/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_68/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_68/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_69/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_69/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_7/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_7/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_70/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_70/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_71/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_71/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_72/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_72/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_73/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_73/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_74/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_74/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_75/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_75/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_76/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_76/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_77/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_77/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_78/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_78/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_79/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_79/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_8/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_8/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_80/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_80/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_81/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_81/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_82/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_82/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_83/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_83/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_84/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_84/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_85/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_85/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_86/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_86/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_87/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_87/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_88/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_88/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_89/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_89/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_9/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_9/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_90/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_90/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_91/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_91/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_92/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_92/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_93/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_93/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_94/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_94/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_95/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_95/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_96/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_96/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_97/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_97/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_98/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_98/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frame_99/delay_sec" type="float" setter="set_frame_delay" getter="get_frame_delay"> + </member> + <member name="frame_99/texture" type="Texture" setter="set_frame_texture" getter="get_frame_texture"> + </member> + <member name="frames" type="int" setter="set_frames" getter="get_frames"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index c6b868c058..74c6796b06 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -552,7 +552,8 @@ <argument index="1" name="path" type="NodePath"> </argument> <description> - Set the path of a track. 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. Tracks that control properties or bones must append their name after the path, separated by ":". Example: "character/skeleton:ankle" or "character/mesh:transform/local" + Set the path of a track. 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. Tracks that control properties or bones must append their name after the path, separated by ":". + [b]Example:[/b] "character/skeleton:ankle" or "character/mesh:transform/local". </description> </method> <method name="track_swap"> diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml new file mode 100644 index 0000000000..2f991699d6 --- /dev/null +++ b/doc/classes/AnimationNode.xml @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNode" inherits="Resource" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="_parent_set" qualifiers="virtual"> + <return type="void"> + </return> + <argument index="0" name="parent" type="Object"> + </argument> + <description> + </description> + </method> + <method name="add_input"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="blend_animation"> + <return type="void"> + </return> + <argument index="0" name="animation" type="String"> + </argument> + <argument index="1" name="time" type="float"> + </argument> + <argument index="2" name="delta" type="float"> + </argument> + <argument index="3" name="seeked" type="bool"> + </argument> + <argument index="4" name="blend" type="float"> + </argument> + <description> + </description> + </method> + <method name="blend_input"> + <return type="float"> + </return> + <argument index="0" name="input_index" type="int"> + </argument> + <argument index="1" name="time" type="float"> + </argument> + <argument index="2" name="seek" type="bool"> + </argument> + <argument index="3" name="blend" type="float"> + </argument> + <argument index="4" name="filter" type="int" enum="AnimationNode.FilterAction" default="0"> + </argument> + <argument index="5" name="optimize" type="bool" default="true"> + </argument> + <description> + </description> + </method> + <method name="blend_node"> + <return type="float"> + </return> + <argument index="0" name="node" type="AnimationNode"> + </argument> + <argument index="1" name="time" type="float"> + </argument> + <argument index="2" name="seek" type="bool"> + </argument> + <argument index="3" name="blend" type="float"> + </argument> + <argument index="4" name="filter" type="int" enum="AnimationNode.FilterAction" default="0"> + </argument> + <argument index="5" name="optimize" type="bool" default="true"> + </argument> + <description> + </description> + </method> + <method name="get_caption" qualifiers="virtual"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_input_activity" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="input" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_input_connection"> + <return type="String"> + </return> + <argument index="0" name="input" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_input_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_input_name"> + <return type="String"> + </return> + <argument index="0" name="input" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_parent" qualifiers="const"> + <return type="AnimationNode"> + </return> + <description> + </description> + </method> + <method name="get_position" qualifiers="const"> + <return type="Vector2"> + </return> + <description> + </description> + </method> + <method name="get_tree" qualifiers="const"> + <return type="AnimationTree"> + </return> + <description> + </description> + </method> + <method name="has_filter" qualifiers="virtual"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="is_path_filtered" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="path" type="NodePath"> + </argument> + <description> + </description> + </method> + <method name="process" qualifiers="virtual"> + <return type="void"> + </return> + <argument index="0" name="time" type="float"> + </argument> + <argument index="1" name="seek" type="bool"> + </argument> + <description> + </description> + </method> + <method name="remove_input"> + <return type="void"> + </return> + <argument index="0" name="index" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_filter_path"> + <return type="void"> + </return> + <argument index="0" name="path" type="NodePath"> + </argument> + <argument index="1" name="enable" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_parent"> + <return type="void"> + </return> + <argument index="0" name="parent" type="Object"> + </argument> + <description> + </description> + </method> + <method name="set_position"> + <return type="void"> + </return> + <argument index="0" name="position" type="Vector2"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="filter_enabled" type="bool" setter="set_filter_enabled" getter="is_filter_enabled"> + </member> + </members> + <signals> + <signal name="removed_from_graph"> + <description> + </description> + </signal> + </signals> + <constants> + <constant name="FILTER_IGNORE" value="0" enum="FilterAction"> + </constant> + <constant name="FILTER_PASS" value="1" enum="FilterAction"> + </constant> + <constant name="FILTER_STOP" value="2" enum="FilterAction"> + </constant> + <constant name="FILTER_BLEND" value="3" enum="FilterAction"> + </constant> + </constants> +</class> diff --git a/doc/classes/AnimationNodeAdd2.xml b/doc/classes/AnimationNodeAdd2.xml new file mode 100644 index 0000000000..267eec6406 --- /dev/null +++ b/doc/classes/AnimationNodeAdd2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeAdd2" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="amount" type="float" setter="set_amount" getter="get_amount"> + </member> + <member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml new file mode 100644 index 0000000000..6596b76f85 --- /dev/null +++ b/doc/classes/AnimationNodeAdd3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeAdd3" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="amount" type="float" setter="set_amount" getter="get_amount"> + </member> + <member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml new file mode 100644 index 0000000000..22f5e0838e --- /dev/null +++ b/doc/classes/AnimationNodeAnimation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeAnimation" inherits="AnimationRootNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_playback_time" qualifiers="const"> + <return type="float"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="animation" type="String" setter="set_animation" getter="get_animation"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml new file mode 100644 index 0000000000..8ef114f69b --- /dev/null +++ b/doc/classes/AnimationNodeBlend2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeBlend2" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="amount" type="float" setter="set_amount" getter="get_amount"> + </member> + <member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeBlend3.xml b/doc/classes/AnimationNodeBlend3.xml new file mode 100644 index 0000000000..094810d008 --- /dev/null +++ b/doc/classes/AnimationNodeBlend3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeBlend3" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="amount" type="float" setter="set_amount" getter="get_amount"> + </member> + <member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml new file mode 100644 index 0000000000..96c94a8972 --- /dev/null +++ b/doc/classes/AnimationNodeBlendSpace1D.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeBlendSpace1D" inherits="AnimationRootNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_blend_point"> + <return type="void"> + </return> + <argument index="0" name="node" type="AnimationRootNode"> + </argument> + <argument index="1" name="pos" type="float"> + </argument> + <argument index="2" name="at_index" type="int" default="-1"> + </argument> + <description> + </description> + </method> + <method name="get_blend_point_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_blend_point_node" qualifiers="const"> + <return type="AnimationRootNode"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_blend_point_position" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_blend_point"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_blend_point_node"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <argument index="1" name="node" type="AnimationRootNode"> + </argument> + <description> + </description> + </method> + <method name="set_blend_point_position"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <argument index="1" name="pos" type="float"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="blend_pos" type="float" setter="set_blend_pos" getter="get_blend_pos"> + </member> + <member name="max_space" type="float" setter="set_max_space" getter="get_max_space"> + </member> + <member name="min_space" type="float" setter="set_min_space" getter="get_min_space"> + </member> + <member name="snap" type="float" setter="set_snap" getter="get_snap"> + </member> + <member name="value_label" type="String" setter="set_value_label" getter="get_value_label"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml new file mode 100644 index 0000000000..31dc7eebac --- /dev/null +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeBlendSpace2D" inherits="AnimationRootNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_blend_point"> + <return type="void"> + </return> + <argument index="0" name="node" type="AnimationRootNode"> + </argument> + <argument index="1" name="pos" type="Vector2"> + </argument> + <argument index="2" name="at_index" type="int" default="-1"> + </argument> + <description> + </description> + </method> + <method name="add_triangle"> + <return type="void"> + </return> + <argument index="0" name="x" type="int"> + </argument> + <argument index="1" name="y" type="int"> + </argument> + <argument index="2" name="z" type="int"> + </argument> + <argument index="3" name="at_index" type="int" default="-1"> + </argument> + <description> + </description> + </method> + <method name="get_blend_point_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_blend_point_node" qualifiers="const"> + <return type="AnimationRootNode"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_blend_point_position" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_triangle_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_triangle_point"> + <return type="int"> + </return> + <argument index="0" name="triangle" type="int"> + </argument> + <argument index="1" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_blend_point"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_triangle"> + <return type="void"> + </return> + <argument index="0" name="triangle" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_blend_point_node"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <argument index="1" name="node" type="AnimationRootNode"> + </argument> + <description> + </description> + </method> + <method name="set_blend_point_position"> + <return type="void"> + </return> + <argument index="0" name="point" type="int"> + </argument> + <argument index="1" name="pos" type="Vector2"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="auto_triangles" type="bool" setter="set_auto_triangles" getter="get_auto_triangles"> + </member> + <member name="blend_position" type="Vector2" setter="set_blend_position" getter="get_blend_position"> + </member> + <member name="max_space" type="Vector2" setter="set_max_space" getter="get_max_space"> + </member> + <member name="min_space" type="Vector2" setter="set_min_space" getter="get_min_space"> + </member> + <member name="snap" type="Vector2" setter="set_snap" getter="get_snap"> + </member> + <member name="x_label" type="String" setter="set_x_label" getter="get_x_label"> + </member> + <member name="y_label" type="String" setter="set_y_label" getter="get_y_label"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeBlendTree.xml b/doc/classes/AnimationNodeBlendTree.xml new file mode 100644 index 0000000000..88257883a3 --- /dev/null +++ b/doc/classes/AnimationNodeBlendTree.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeBlendTree" inherits="AnimationRootNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="node" type="AnimationNode"> + </argument> + <description> + </description> + </method> + <method name="connect_node"> + <return type="void"> + </return> + <argument index="0" name="input_node" type="String"> + </argument> + <argument index="1" name="input_index" type="int"> + </argument> + <argument index="2" name="output_node" type="String"> + </argument> + <description> + </description> + </method> + <method name="disconnect_node"> + <return type="void"> + </return> + <argument index="0" name="input_node" type="String"> + </argument> + <argument index="1" name="input_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_node" qualifiers="const"> + <return type="AnimationNode"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="has_node" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="remove_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="rename_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="new_name" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="graph_offset" type="Vector2" setter="set_graph_offset" getter="get_graph_offset"> + </member> + </members> + <constants> + <constant name="CONNECTION_OK" value="0"> + </constant> + <constant name="CONNECTION_ERROR_NO_INPUT" value="1"> + </constant> + <constant name="CONNECTION_ERROR_NO_INPUT_INDEX" value="2"> + </constant> + <constant name="CONNECTION_ERROR_NO_OUTPUT" value="3"> + </constant> + <constant name="CONNECTION_ERROR_SAME_NODE" value="4"> + </constant> + <constant name="CONNECTION_ERROR_CONNECTION_EXISTS" value="5"> + </constant> + </constants> +</class> diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml new file mode 100644 index 0000000000..b59b6e2b83 --- /dev/null +++ b/doc/classes/AnimationNodeOneShot.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeOneShot" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_mix_mode" qualifiers="const"> + <return type="int" enum="AnimationNodeOneShot.MixMode"> + </return> + <description> + </description> + </method> + <method name="is_active" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="set_mix_mode"> + <return type="void"> + </return> + <argument index="0" name="mode" type="int" enum="AnimationNodeOneShot.MixMode"> + </argument> + <description> + </description> + </method> + <method name="start"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="stop"> + <return type="void"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="autorestart" type="bool" setter="set_autorestart" getter="has_autorestart"> + </member> + <member name="autorestart_delay" type="float" setter="set_autorestart_delay" getter="get_autorestart_delay"> + </member> + <member name="autorestart_random_delay" type="float" setter="set_autorestart_random_delay" getter="get_autorestart_random_delay"> + </member> + <member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time"> + </member> + <member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time"> + </member> + <member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync"> + </member> + </members> + <constants> + <constant name="MIX_MODE_BLEND" value="0" enum="MixMode"> + </constant> + <constant name="MIX_MODE_ADD" value="1" enum="MixMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/AnimationNodeOutput.xml b/doc/classes/AnimationNodeOutput.xml new file mode 100644 index 0000000000..98a41bc9cb --- /dev/null +++ b/doc/classes/AnimationNodeOutput.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeOutput" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml new file mode 100644 index 0000000000..ad40f20db1 --- /dev/null +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeStateMachine" inherits="AnimationRootNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="node" type="AnimationNode"> + </argument> + <description> + </description> + </method> + <method name="add_transition"> + <return type="void"> + </return> + <argument index="0" name="from" type="String"> + </argument> + <argument index="1" name="to" type="String"> + </argument> + <argument index="2" name="transition" type="AnimationNodeStateMachineTransition"> + </argument> + <description> + </description> + </method> + <method name="get_current_node" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_end_node" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_graph_offset" qualifiers="const"> + <return type="Vector2"> + </return> + <description> + </description> + </method> + <method name="get_node" qualifiers="const"> + <return type="AnimationNode"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="get_node_name" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="node" type="AnimationNode"> + </argument> + <description> + </description> + </method> + <method name="get_start_node" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> + <method name="get_transition" qualifiers="const"> + <return type="AnimationNodeStateMachineTransition"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_transition_count" qualifiers="const"> + <return type="int"> + </return> + <description> + </description> + </method> + <method name="get_transition_from" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_transition_to" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_travel_path" qualifiers="const"> + <return type="PoolStringArray"> + </return> + <description> + </description> + </method> + <method name="has_node" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="has_transition"> + <return type="void"> + </return> + <argument index="0" name="from" type="String"> + </argument> + <argument index="1" name="to" type="String"> + </argument> + <argument index="2" name="arg2" type="AnimationNodeStateMachineTransition"> + </argument> + <description> + </description> + </method> + <method name="is_playing" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="remove_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="remove_transition"> + <return type="void"> + </return> + <argument index="0" name="from" type="String"> + </argument> + <argument index="1" name="to" type="String"> + </argument> + <description> + </description> + </method> + <method name="remove_transition_by_index"> + <return type="void"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> + <method name="rename_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <argument index="1" name="new_name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_end_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="set_graph_offset"> + <return type="void"> + </return> + <argument index="0" name="name" type="Vector2"> + </argument> + <description> + </description> + </method> + <method name="set_start_node"> + <return type="void"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + </description> + </method> + <method name="start"> + <return type="void"> + </return> + <argument index="0" name="node" type="String"> + </argument> + <description> + </description> + </method> + <method name="stop"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="travel"> + <return type="bool"> + </return> + <argument index="0" name="to_node" type="String"> + </argument> + <description> + </description> + </method> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml new file mode 100644 index 0000000000..280a1413b8 --- /dev/null +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeStateMachineTransition" inherits="Resource" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance"> + </member> + <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled"> + </member> + <member name="priority" type="int" setter="set_priority" getter="get_priority"> + </member> + <member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode"> + </member> + <member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time"> + </member> + </members> + <constants> + <constant name="SWITCH_MODE_IMMEDIATE" value="0" enum="SwitchMode"> + </constant> + <constant name="SWITCH_MODE_SYNC" value="1" enum="SwitchMode"> + </constant> + <constant name="SWITCH_MODE_AT_END" value="2" enum="SwitchMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/AnimationNodeTimeScale.xml b/doc/classes/AnimationNodeTimeScale.xml new file mode 100644 index 0000000000..b3e90d190b --- /dev/null +++ b/doc/classes/AnimationNodeTimeScale.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeTimeScale" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="scale" type="float" setter="set_scale" getter="get_scale"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeTimeSeek.xml b/doc/classes/AnimationNodeTimeSeek.xml new file mode 100644 index 0000000000..a479208128 --- /dev/null +++ b/doc/classes/AnimationNodeTimeSeek.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeTimeSeek" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="seek_pos" type="float" setter="set_seek_pos" getter="get_seek_pos"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml new file mode 100644 index 0000000000..3731fc05ed --- /dev/null +++ b/doc/classes/AnimationNodeTransition.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationNodeTransition" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="current" type="int" setter="set_current" getter="get_current"> + </member> + <member name="input_0/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_0/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_1/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_1/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_10/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_10/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_11/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_11/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_12/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_12/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_13/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_13/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_14/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_14/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_15/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_15/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_16/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_16/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_17/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_17/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_18/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_18/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_19/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_19/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_2/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_2/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_20/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_20/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_21/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_21/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_22/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_22/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_23/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_23/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_24/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_24/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_25/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_25/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_26/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_26/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_27/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_27/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_28/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_28/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_29/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_29/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_3/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_3/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_30/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_30/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_31/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_31/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_4/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_4/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_5/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_5/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_6/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_6/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_7/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_7/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_8/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_8/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_9/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance"> + </member> + <member name="input_9/name" type="String" setter="set_input_caption" getter="get_input_caption"> + </member> + <member name="input_count" type="int" setter="set_enabled_inputs" getter="get_enabled_inputs"> + </member> + <member name="xfade_time" type="float" setter="set_cross_fade_time" getter="get_cross_fade_time"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index e1b3c7a9c9..f93590bb9d 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -271,6 +271,10 @@ Notifies when an animation starts playing. </description> </signal> + <signal name="caches_cleared"> + <description> + </description> + </signal> </signals> <constants> <constant name="ANIMATION_PROCESS_PHYSICS" value="0" enum="AnimationProcessMode"> diff --git a/doc/classes/AnimationRootNode.xml b/doc/classes/AnimationRootNode.xml new file mode 100644 index 0000000000..dab2c12373 --- /dev/null +++ b/doc/classes/AnimationRootNode.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationRootNode" inherits="AnimationNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml new file mode 100644 index 0000000000..a8e3a821b1 --- /dev/null +++ b/doc/classes/AnimationTree.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="AnimationTree" inherits="Node" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_root_motion_transform" qualifiers="const"> + <return type="Transform"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="active" type="bool" setter="set_active" getter="is_active"> + </member> + <member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player"> + </member> + <member name="process_mode" type="int" setter="set_process_mode" getter="get_process_mode" enum="AnimationTree.AnimationProcessMode"> + </member> + <member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track"> + </member> + <member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root"> + </member> + </members> + <constants> + <constant name="ANIMATION_PROCESS_PHYSICS" value="0" enum="AnimationProcessMode"> + </constant> + <constant name="ANIMATION_PROCESS_IDLE" value="1" enum="AnimationProcessMode"> + </constant> + </constants> +</class> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 7fcb827252..9c5ae8ebd0 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -300,7 +300,8 @@ <argument index="1" name="func" type="String"> </argument> <description> - Sort the array using a custom method. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return true if the first argument is less than the second, and return false otherwise. Note: you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior. + Sort the array using a custom method. The arguments are an object that holds the method and the name of such method. The custom method receives two arguments (a pair of elements from the array) and must return true if the first argument is less than the second, and return false otherwise. + [b]Note:[/b] you cannot randomize the return value as the heapsort algorithm expects a deterministic result. Doing so will result in unexpected behavior. [codeblock] class MyCustomSorter: static func sort(a, b): diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 80d7b7783f..5ccf0b55aa 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -26,7 +26,7 @@ </argument> <argument index="2" name="blend_shapes" type="Array" default="[ ]"> </argument> - <argument index="3" name="compress_flags" type="int" default="97792"> + <argument index="3" name="compress_flags" type="int" default="97280"> </argument> <description> Creates a new surface. @@ -40,18 +40,21 @@ <return type="void"> </return> <description> + Centers the geometry. </description> </method> <method name="clear_blend_shapes"> <return type="void"> </return> <description> + Remove all blend shapes from this [code]ArrayMesh[/code]. </description> </method> <method name="get_blend_shape_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the number of blend shapes that the [code]ArrayMesh[/code] holds. </description> </method> <method name="get_blend_shape_name" qualifiers="const"> @@ -60,6 +63,7 @@ <argument index="0" name="index" type="int"> </argument> <description> + Returns the name of the blend shape at this index. </description> </method> <method name="get_surface_count" qualifiers="const"> @@ -77,12 +81,23 @@ <argument index="1" name="arg1" type="float"> </argument> <description> + Will perform a UV unwrap on the [code]ArrayMesh[/code] to prepare the mesh for lightmapping. </description> </method> <method name="regen_normalmaps"> <return type="void"> </return> <description> + Will regenerate normal maps for the [code]ArrayMesh[/code]. + </description> + </method> + <method name="surface_find_by_name" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="name" type="String"> + </argument> + <description> + Return the index of the first surface with this name held within this [code]ArrayMesh[/code]. If none are found -1 is returned. </description> </method> <method name="surface_get_array_index_len" qualifiers="const"> @@ -109,6 +124,7 @@ <argument index="0" name="surf_idx" type="int"> </argument> <description> + Returns the arrays for the vertices, normals, uvs, etc. that make up the requested surface (see [method add_surface_from_arrays]). </description> </method> <method name="surface_get_blend_shape_arrays" qualifiers="const"> @@ -117,6 +133,7 @@ <argument index="0" name="surf_idx" type="int"> </argument> <description> + Returns the blend shape arrays for the requested surface. </description> </method> <method name="surface_get_format" qualifiers="const"> @@ -143,6 +160,7 @@ <argument index="0" name="surf_idx" type="int"> </argument> <description> + Get the name assigned to this surface. </description> </method> <method name="surface_get_primitive_type" qualifiers="const"> @@ -171,6 +189,7 @@ <argument index="1" name="material" type="Material"> </argument> <description> + Set a [Material] for a given surface. Surface will be rendered using this material. </description> </method> <method name="surface_set_name"> @@ -181,7 +200,7 @@ <argument index="1" name="name" type="String"> </argument> <description> - Set a [Material] for a given surface. Surface will be rendered using this material. + Set a name for a given surface. </description> </method> <method name="surface_update_region"> @@ -201,6 +220,7 @@ <member name="blend_shape_mode" type="int" setter="set_blend_shape_mode" getter="get_blend_shape_mode" enum="Mesh.BlendShapeMode"> </member> <member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb"> + An overriding bounding box for this mesh. </member> </members> <constants> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 15bbb1625c..753a506058 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -16,6 +16,7 @@ <return type="float"> </return> <description> + Returns the length of the audio stream in seconds. </description> </method> </methods> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index 00d03d2b20..26d0b1a83d 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -17,7 +17,7 @@ <return type="float"> </return> <description> - Returns the position in the [AudioStream]. + Returns the position in the [AudioStream] in seconds. </description> </method> <method name="play"> @@ -65,6 +65,8 @@ <member name="stream" type="AudioStream" setter="set_stream" getter="get_stream"> The [AudioStream] object to be played. </member> + <member name="stream_paused" type="bool" setter="set_stream_paused" getter="get_stream_paused"> + </member> <member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db"> Volume of sound, in dB. </member> diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml index 03b0a3aa81..5cc87e0e7a 100644 --- a/doc/classes/AudioStreamPlayer2D.xml +++ b/doc/classes/AudioStreamPlayer2D.xml @@ -71,6 +71,8 @@ <member name="stream" type="AudioStream" setter="set_stream" getter="get_stream"> The [AudioStream] object to be played. </member> + <member name="stream_paused" type="bool" setter="set_stream_paused" getter="get_stream_paused"> + </member> <member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db"> Base volume without dampening. </member> diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml index 2746938c1d..e61515ffc0 100644 --- a/doc/classes/AudioStreamPlayer3D.xml +++ b/doc/classes/AudioStreamPlayer3D.xml @@ -95,6 +95,8 @@ <member name="stream" type="AudioStream" setter="set_stream" getter="get_stream"> The [AudioStream] object to be played. </member> + <member name="stream_paused" type="bool" setter="set_stream_paused" getter="get_stream_paused"> + </member> <member name="unit_db" type="float" setter="set_unit_db" getter="get_unit_db"> Base sound level unaffected by dampening, in dB. </member> diff --git a/doc/classes/CPUParticles.xml b/doc/classes/CPUParticles.xml new file mode 100644 index 0000000000..9d3dc5d70a --- /dev/null +++ b/doc/classes/CPUParticles.xml @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CPUParticles" inherits="GeometryInstance" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="convert_from_particles"> + <return type="void"> + </return> + <argument index="0" name="particles" type="Node"> + </argument> + <description> + </description> + </method> + <method name="restart"> + <return type="void"> + </return> + <description> + </description> + </method> + </methods> + <members> + <member name="amount" type="int" setter="set_amount" getter="get_amount"> + </member> + <member name="angle" type="float" setter="set_param" getter="get_param"> + </member> + <member name="angle_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="angle_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="angular_velocity" type="float" setter="set_param" getter="get_param"> + </member> + <member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="angular_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="anim_loop" type="bool" setter="set_particle_flag" getter="get_particle_flag"> + </member> + <member name="anim_offset" type="float" setter="set_param" getter="get_param"> + </member> + <member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="anim_offset_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="anim_speed" type="float" setter="set_param" getter="get_param"> + </member> + <member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="anim_speed_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="color" type="Color" setter="set_color" getter="get_color"> + </member> + <member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp"> + </member> + <member name="damping" type="float" setter="set_param" getter="get_param"> + </member> + <member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="damping_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="CPUParticles.DrawOrder"> + </member> + <member name="emission_box_extents" type="Vector3" setter="set_emission_box_extents" getter="get_emission_box_extents"> + </member> + <member name="emission_colors" type="PoolColorArray" setter="set_emission_colors" getter="get_emission_colors"> + </member> + <member name="emission_normals" type="PoolVector3Array" setter="set_emission_normals" getter="get_emission_normals"> + </member> + <member name="emission_points" type="PoolVector3Array" setter="set_emission_points" getter="get_emission_points"> + </member> + <member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles.EmissionShape"> + </member> + <member name="emission_sphere_radius" type="float" setter="set_emission_sphere_radius" getter="get_emission_sphere_radius"> + </member> + <member name="emitting" type="bool" setter="set_emitting" getter="is_emitting"> + </member> + <member name="explosiveness" type="float" setter="set_explosiveness_ratio" getter="get_explosiveness_ratio"> + </member> + <member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps"> + </member> + <member name="flag_align_y" type="bool" setter="set_particle_flag" getter="get_particle_flag"> + </member> + <member name="flag_disable_z" type="bool" setter="set_particle_flag" getter="get_particle_flag"> + </member> + <member name="flag_rotate_y" type="bool" setter="set_particle_flag" getter="get_particle_flag"> + </member> + <member name="flatness" type="float" setter="set_flatness" getter="get_flatness"> + </member> + <member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta"> + </member> + <member name="gravity" type="Vector3" setter="set_gravity" getter="get_gravity"> + </member> + <member name="hue_variation" type="float" setter="set_param" getter="get_param"> + </member> + <member name="hue_variation_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="hue_variation_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="initial_velocity" type="float" setter="set_param" getter="get_param"> + </member> + <member name="initial_velocity_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime"> + </member> + <member name="linear_accel" type="float" setter="set_param" getter="get_param"> + </member> + <member name="linear_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="linear_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates"> + </member> + <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> + </member> + <member name="one_shot" type="bool" setter="set_one_shot" getter="get_one_shot"> + </member> + <member name="preprocess" type="float" setter="set_pre_process_time" getter="get_pre_process_time"> + </member> + <member name="radial_accel" type="float" setter="set_param" getter="get_param"> + </member> + <member name="radial_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="radial_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="randomness" type="float" setter="set_randomness_ratio" getter="get_randomness_ratio"> + </member> + <member name="scale" type="float" setter="set_param" getter="get_param"> + </member> + <member name="scale_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="scale_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale"> + </member> + <member name="spread" type="float" setter="set_spread" getter="get_spread"> + </member> + <member name="tangential_accel" type="float" setter="set_param" getter="get_param"> + </member> + <member name="tangential_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> + </member> + <member name="tangential_accel_random" type="float" setter="set_param_randomness" getter="get_param_randomness"> + </member> + </members> + <constants> + <constant name="DRAW_ORDER_INDEX" value="0" enum="DrawOrder"> + </constant> + <constant name="DRAW_ORDER_LIFETIME" value="1" enum="DrawOrder"> + </constant> + <constant name="DRAW_ORDER_VIEW_DEPTH" value="2" enum="DrawOrder"> + </constant> + <constant name="PARAM_INITIAL_LINEAR_VELOCITY" value="0" enum="Parameter"> + </constant> + <constant name="PARAM_ANGULAR_VELOCITY" value="1" enum="Parameter"> + </constant> + <constant name="PARAM_LINEAR_ACCEL" value="2" enum="Parameter"> + </constant> + <constant name="PARAM_RADIAL_ACCEL" value="3" enum="Parameter"> + </constant> + <constant name="PARAM_TANGENTIAL_ACCEL" value="4" enum="Parameter"> + </constant> + <constant name="PARAM_DAMPING" value="5" enum="Parameter"> + </constant> + <constant name="PARAM_ANGLE" value="6" enum="Parameter"> + </constant> + <constant name="PARAM_SCALE" value="7" enum="Parameter"> + </constant> + <constant name="PARAM_HUE_VARIATION" value="8" enum="Parameter"> + </constant> + <constant name="PARAM_ANIM_SPEED" value="9" enum="Parameter"> + </constant> + <constant name="PARAM_ANIM_OFFSET" value="10" enum="Parameter"> + </constant> + <constant name="PARAM_MAX" value="11" enum="Parameter"> + </constant> + <constant name="FLAG_ALIGN_Y_TO_VELOCITY" value="0" enum="Flags"> + </constant> + <constant name="FLAG_ROTATE_Y" value="1" enum="Flags"> + </constant> + <constant name="FLAG_MAX" value="4" enum="Flags"> + </constant> + <constant name="EMISSION_SHAPE_POINT" value="0" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_SPHERE" value="1" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_BOX" value="2" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_POINTS" value="3" enum="EmissionShape"> + </constant> + <constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape"> + </constant> + </constants> +</class> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 104c9e0a98..c675bbe994 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -5,7 +5,7 @@ </brief_description> <description> A color is represented as red, green and blue (r,g,b) components. Additionally, "a" represents the alpha component, often used for transparency. Values are in floating point and usually range from 0 to 1. Some methods (such as set_modulate(color)) may accept values > 1. - You can also create a color from standardised color names with [method @GDScript.ColorN]. + You can also create a color from standardised color names with Color.ColorN (e.g. Color.green) or [method @GDScript.ColorN]. </description> <tutorials> </tutorials> @@ -15,67 +15,67 @@ <method name="Color"> <return type="Color"> </return> - <argument index="0" name="r" type="float"> - </argument> - <argument index="1" name="g" type="float"> - </argument> - <argument index="2" name="b" type="float"> - </argument> - <argument index="3" name="a" type="float"> + <argument index="0" name="from" type="String"> </argument> <description> - Constructs a color from an RGBA profile using values between 0 and 1 (float). + Constructs a color from an HTML hexadecimal color string in ARGB or RGB format. See also [method @GDScript.ColorN]. + The following string formats are supported: + [code]"#ff00ff00"[/code] - ARGB format with '#' + [code]"ff00ff00"[/code] - ARGB format + [code]"#ff00ff"[/code] - RGB format with '#' + [code]"ff00ff"[/code] - RGB format [codeblock] - var c = Color(0.2, 1.0, .7, .8) # a color of an RGBA(51, 255, 178, 204) + # The following code creates the same color of an RGBA(178, 217, 10, 255) + var c1 = Color("#ffb2d90a") # ARGB format with '#' + var c2 = Color("ffb2d90a") # ARGB format + var c3 = Color("#b2d90a") # RGB format with '#' + var c4 = Color("b2d90a") # RGB format [/codeblock] </description> </method> <method name="Color"> <return type="Color"> </return> - <argument index="0" name="r" type="float"> - </argument> - <argument index="1" name="g" type="float"> - </argument> - <argument index="2" name="b" type="float"> + <argument index="0" name="from" type="int"> </argument> <description> - Constructs a color from an RGB profile using values between 0 and 1 (float). Alpha will always be 1. + Constructs a color from a 32-bit integer (each byte represents a component of the RGBA profile). [codeblock] - var c = Color(0.2, 1.0, .7) # a color of an RGBA(51, 255, 178, 255) + var c = Color(274) # a color of an RGBA(0, 0, 1, 18) [/codeblock] </description> </method> <method name="Color"> <return type="Color"> </return> - <argument index="0" name="from" type="int"> + <argument index="0" name="r" type="float"> + </argument> + <argument index="1" name="g" type="float"> + </argument> + <argument index="2" name="b" type="float"> </argument> <description> - Constructs a color from a 32-bit integer (each byte represents a component of the RGBA profile). + Constructs a color from an RGB profile using values between 0 and 1 (float). Alpha will always be 1. [codeblock] - var c = Color(274) # a color of an RGBA(0, 0, 1, 18) + var c = Color(0.2, 1.0, .7) # a color of an RGBA(51, 255, 178, 255) [/codeblock] </description> </method> <method name="Color"> <return type="Color"> </return> - <argument index="0" name="from" type="String"> + <argument index="0" name="r" type="float"> + </argument> + <argument index="1" name="g" type="float"> + </argument> + <argument index="2" name="b" type="float"> + </argument> + <argument index="3" name="a" type="float"> </argument> <description> - Constructs a color from an HTML hexadecimal color string in ARGB or RGB format. See also [method @GDScript.ColorN]. - The following string formats are supported: - [code]"#ff00ff00"[/code] - ARGB format with '#' - [code]"ff00ff00"[/code] - ARGB format - [code]"#ff00ff"[/code] - RGB format with '#' - [code]"ff00ff"[/code] - RGB format + Constructs a color from an RGBA profile using values between 0 and 1 (float). [codeblock] - # The following code creates the same color of an RGBA(178, 217, 10, 255) - var c1 = Color("#ffb2d90a") # ARGB format with '#' - var c2 = Color("ffb2d90a") # ARGB format - var c3 = Color("#b2d90a") # RGB format with '#' - var c4 = Color("b2d90a") # RGB format + var c = Color(0.2, 1.0, .7, .8) # a color of an RGBA(51, 255, 178, 204) [/codeblock] </description> </method> @@ -187,14 +187,47 @@ [/codeblock] </description> </method> + <method name="to_abgr32"> + <return type="int"> + </return> + <description> + Returns the color's 32-bit integer in ABGR format (each byte represents a component of the ABGR profile). ABGR is the reversed version of the default format. + [codeblock] + var c = Color(1, .5, .2) + print(c.to_abgr32()) # Prints 4281565439 + [/codeblock] + </description> + </method> + <method name="to_abgr64"> + <return type="int"> + </return> + <description> + Returns the color's 64-bit integer in ABGR format (each word represents a component of the ABGR profile). ABGR is the reversed version of the default format. + [codeblock] + var c = Color(1, .5, .2) + print(c.to_abgr64()) # Prints -225178692812801 + [/codeblock] + </description> + </method> <method name="to_argb32"> <return type="int"> </return> <description> - Returns the color's 32-bit integer in ARGB format (each byte represents a component of the ARGB profile). More compatible with DirectX. + Returns the color's 32-bit integer in ARGB format (each byte represents a component of the ARGB profile). ARGB is more compatible with DirectX. [codeblock] var c = Color(1, .5, .2) - print(str(c.to_32())) # prints 4294934323 + print(c.to_argb32()) # Prints 4294934323 + [/codeblock] + </description> + </method> + <method name="to_argb64"> + <return type="int"> + </return> + <description> + Returns the color's 64-bit integer in ARGB format (each word represents a component of the ARGB profile). ARGB is more compatible with DirectX. + [codeblock] + var c = Color(1, .5, .2) + print(c.to_argb64()) # Prints -2147470541 [/codeblock] </description> </method> @@ -217,12 +250,22 @@ <return type="int"> </return> <description> - Returns the color's 32-bit integer in ARGB format (each byte represents a component of the ARGB profile). + Returns the color's 32-bit integer in RGBA format (each byte represents a component of the RGBA profile). RGBA is the format that Godot uses by default. + [codeblock] + var c = Color(1, .5, .2) + print(c.to_rgba32()) # Prints 4286526463 + [/codeblock] + </description> + </method> + <method name="to_rgba64"> + <return type="int"> + </return> + <description> + Returns the color's 64-bit integer in RGBA format (each word represents a component of the RGBA profile). RGBA is the format that Godot uses by default. [codeblock] var c = Color(1, .5, .2) - print(str(c.to_32())) # prints 4294934323 + print(c.to_rgba64()) # Prints -140736629309441 [/codeblock] - [i]This is same as [method to_argb32] but may be changed later to support RGBA format instead[/i]. </description> </method> </methods> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 1bd902c20e..232357f822 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -25,15 +25,15 @@ <member name="color" type="Color" setter="set_pick_color" getter="get_pick_color"> The currently selected color. </member> + <member name="deferred_mode" type="bool" setter="set_deferred_mode" getter="is_deferred_mode"> + If [code]true[/code], the color will apply only after user releases mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues). + </member> <member name="edit_alpha" type="bool" setter="set_edit_alpha" getter="is_editing_alpha"> If [code]true[/code], shows an alpha channel slider (transparency). </member> <member name="raw_mode" type="bool" setter="set_raw_mode" getter="is_raw_mode"> If [code]true[/code], allows the color R, G, B component values to go beyond 1.0, which can be used for certain special operations that require it (like tinting without darkening or rendering sprites in HDR). </member> - <member name="deferred_mode" type="bool" setter="set_deferred_mode" getter="is_deferred_mode"> - If [code]true[/code], the color will apply only after user releases mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues). - </member> </members> <signals> <signal name="color_changed"> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index 9d7df14014..d049e936a8 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -18,7 +18,7 @@ Returns the [code]ColorPicker[/code] that this [code]ColorPickerButton[/code] toggles. </description> </method> - <method name="get_popup" qualifiers="const"> + <method name="get_popup"> <return type="PopupPanel"> </return> <description> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 52382337cf..d11b369e68 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -36,6 +36,14 @@ Replaces Godot 2's [code]_input_event[/code]. </description> </method> + <method name="_make_custom_tooltip" qualifiers="virtual"> + <return type="Object"> + </return> + <argument index="0" name="for_text" type="String"> + </argument> + <description> + </description> + </method> <method name="accept_event"> <return type="void"> </return> @@ -653,6 +661,7 @@ </member> <member name="mouse_default_cursor_shape" type="int" setter="set_default_cursor_shape" getter="get_default_cursor_shape" enum="Control.CursorShape"> The default cursor shape for this control. Useful for Godot plugins and applications or games that use the system's mouse cursors. + [b]Note:[/b] On Linux, shapes may vary depending on the cursor theme of the system. </member> <member name="mouse_filter" type="int" setter="set_mouse_filter" getter="get_mouse_filter" enum="Control.MouseFilter"> Controls whether the control will be able to receive mouse button input events through [method _gui_input] and how these events should be handled. Use one of the [code]MOUSE_FILTER_*[/code] constants. See the constants to learn what each does. diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index 347acce2dd..5a8b506f9e 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -73,6 +73,12 @@ Update a file information. Call this if an external program (not Godot) modified the file. </description> </method> + <method name="update_script_classes"> + <return type="void"> + </return> + <description> + </description> + </method> </methods> <signals> <signal name="filesystem_changed"> diff --git a/doc/classes/EditorFileSystemDirectory.xml b/doc/classes/EditorFileSystemDirectory.xml index f5d0471037..bb3ff91639 100644 --- a/doc/classes/EditorFileSystemDirectory.xml +++ b/doc/classes/EditorFileSystemDirectory.xml @@ -63,6 +63,22 @@ Returns the path to the file at index [code]idx[/code]. </description> </method> + <method name="get_file_script_class_extends" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_file_script_class_name" qualifiers="const"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> <method name="get_file_type" qualifiers="const"> <return type="String"> </return> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index e48eb82691..b21d402468 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -35,19 +35,20 @@ func get_import_options(i): return [{"name": "my_option", "default_value": false}] - func load(src, dst, opts, r_platform_variants, r_gen_files): + func import(source_file, save_path, options, r_platform_variants, r_gen_files): var file = File.new() - if file.open(src, File.READ) != OK: + if file.open(source_file, File.READ) != OK: return FAILED var mesh = Mesh.new() - var save = dst + "." + get_save_extension() - ResourceSaver.save(file, mesh) + var filename = save_path + "." + get_save_extension() + ResourceSaver.save(filename, mesh) return OK [/codeblock] </description> <tutorials> + <link>http://docs.godotengine.org/en/3.0/tutorials/plugins/editor/import_plugins.html</link> </tutorials> <demos> </demos> @@ -119,7 +120,7 @@ <return type="String"> </return> <description> - Get the godot resource type associated with this loader. e.g. "Mesh" or "Animation". + Get the Godot resource type associated with this loader. e.g. "Mesh" or "Animation". </description> </method> <method name="get_save_extension" qualifiers="virtual"> diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 381eef5a40..a2a39fc8b6 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -9,6 +9,12 @@ <demos> </demos> <methods> + <method name="refresh"> + <return type="void"> + </return> + <description> + </description> + </method> </methods> <signals> <signal name="object_id_selected"> @@ -17,12 +23,24 @@ <description> </description> </signal> + <signal name="property_edited"> + <argument index="0" name="property" type="String"> + </argument> + <description> + </description> + </signal> <signal name="property_keyed"> <argument index="0" name="property" type="String"> </argument> <description> </description> </signal> + <signal name="property_selected"> + <argument index="0" name="property" type="String"> + </argument> + <description> + </description> + </signal> <signal name="resource_selected"> <argument index="0" name="res" type="Object"> </argument> @@ -31,6 +49,10 @@ <description> </description> </signal> + <signal name="restart_requested"> + <description> + </description> + </signal> </signals> <constants> </constants> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 9b5452ec14..32d3a2703d 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -21,6 +21,12 @@ <description> </description> </method> + <method name="get_tooltip_text" qualifiers="const"> + <return type="String"> + </return> + <description> + </description> + </method> <method name="update_property" qualifiers="virtual"> <return type="void"> </return> diff --git a/doc/classes/EditorScenePostImport.xml b/doc/classes/EditorScenePostImport.xml index f95c26c2b0..09cae25403 100644 --- a/doc/classes/EditorScenePostImport.xml +++ b/doc/classes/EditorScenePostImport.xml @@ -7,7 +7,7 @@ The imported scene can be automatically modified right after import by specifying a 'custom script' that inherits from this class. The [method post_import]-method receives the imported scene's root-node and returns the modified version of the scene </description> <tutorials> - http://docs.godotengine.org/en/latest/learning/workflow/assets/importing_scenes.html?highlight=post%20import + <link>http://docs.godotengine.org/en/latest/learning/workflow/assets/importing_scenes.html?highlight=post%20import</link> </tutorials> <demos> [codeblock] diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index cd2584ed43..566322e6c5 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -203,6 +203,8 @@ </member> <member name="ss_reflections_roughness" type="bool" setter="set_ssr_rough" getter="is_ssr_rough"> </member> + <member name="ssao_ao_channel_affect" type="float" setter="set_ssao_ao_channel_affect" getter="get_ssao_ao_channel_affect"> + </member> <member name="ssao_bias" type="float" setter="set_ssao_bias" getter="get_ssao_bias"> </member> <member name="ssao_blur" type="int" setter="set_ssao_blur" getter="is_ssao_blur_enabled" enum="Environment.SSAOBlur"> diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml index aea1882dae..8831805dde 100644 --- a/doc/classes/GeometryInstance.xml +++ b/doc/classes/GeometryInstance.xml @@ -59,7 +59,7 @@ Will allow the GeometryInstance to be used when baking lights using a [GIProbe] and/or any other form of baked lighting. Added documentation for GeometryInstance and VisualInstance </constant> - <constant name="FLAG_MAX" value="1" enum="Flags"> + <constant name="FLAG_MAX" value="2" enum="Flags"> </constant> </constants> </class> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 605efd9114..48e3c295f1 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -85,6 +85,12 @@ Return an Array containing the list of connections. A connection consists in a structure of the form {from_slot: 0, from: "GraphNode name 0", to_slot: 1, to: "GraphNode name 1" } </description> </method> + <method name="get_zoom_hbox"> + <return type="HBoxContainer"> + </return> + <description> + </description> + </method> <method name="is_node_connected"> <return type="bool"> </return> @@ -140,6 +146,22 @@ Removes the possibility to disconnect nodes when dragging from the slot at the right if it has the specified type. </description> </method> + <method name="set_connection_activity"> + <return type="void"> + </return> + <argument index="0" name="from" type="String"> + </argument> + <argument index="1" name="from_port" type="int"> + </argument> + <argument index="2" name="to" type="String"> + </argument> + <argument index="3" name="to_port" type="int"> + </argument> + <argument index="4" name="amount" type="float"> + </argument> + <description> + </description> + </method> <method name="set_selected"> <return type="void"> </return> @@ -248,6 +270,8 @@ <constants> </constants> <theme_items> + <theme_item name="activity" type="Color"> + </theme_item> <theme_item name="bezier_len_neg" type="int"> </theme_item> <theme_item name="bezier_len_pos" type="int"> @@ -262,6 +286,10 @@ </theme_item> <theme_item name="more" type="Texture"> </theme_item> + <theme_item name="port_grab_distance_horizontal" type="int"> + </theme_item> + <theme_item name="port_grab_distance_vertical" type="int"> + </theme_item> <theme_item name="reset" type="Texture"> </theme_item> <theme_item name="snap" type="Texture"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index e10485a6b2..55693bd49c 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -593,6 +593,12 @@ </constant> <constant name="INTERPOLATE_CUBIC" value="2" enum="Interpolation"> </constant> + <constant name="INTERPOLATE_TRILINEAR" value="3" enum="Interpolation"> + Performs bilinear separately on the two most suited mipmap levels, then linearly interpolates between them. + It's slower than [code]INTERPOLATE_BILINEAR[/code], but produces higher quality results, with much less aliasing artifacts. + If the image does not have mipmaps, they will be generated and used internally, but no mipmaps will be generated on the resulting image. (Note that if you intend to scale multiple copies of the original image, it's better to call [code]generate_mipmaps[/code] on it in advance, to avoid wasting processing power in generating them again and again.) + On the other hand, if the image already has mipmaps, they will be used, and a new set will be generated for the resulting image. + </constant> <constant name="ALPHA_NONE" value="0" enum="AlphaMode"> </constant> <constant name="ALPHA_BIT" value="1" enum="AlphaMode"> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index bd1d6be4f5..48ca0ddc01 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -4,10 +4,8 @@ Control that provides a list of selectable items (and/or icons) in a single column, or optionally in multiple columns. </brief_description> <description> - This control provides a selectable list of items that may be in a single (or multiple columns) with option of text, icons, - or both text and icon. Tooltips are supported and may be different for every item in the list. Selectable items in the list - may be selected or deselected and multiple selection may be enabled. Selection with right mouse button may also be enabled - to allow use of popup context menus. Items may also be 'activated' with a double click (or Enter key). + This control provides a selectable list of items that may be in a single (or multiple columns) with option of text, icons, or both text and icon. Tooltips are supported and may be different for every item in the list. + Selectable items in the list may be selected or deselected and multiple selection may be enabled. Selection with right mouse button may also be enabled to allow use of popup context menus. Items may also be 'activated' with a double click (or Enter key). </description> <tutorials> </tutorials> diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index ae33ed5205..e6d537b895 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -76,13 +76,13 @@ </argument> <argument index="1" name="floor_normal" type="Vector3" default="Vector3( 0, 0, 0 )"> </argument> - <argument index="2" name="infinite_inertia" type="bool" default="true"> + <argument index="2" name="slope_stop_min_velocity" type="float" default="0.05"> </argument> - <argument index="3" name="slope_stop_min_velocity" type="float" default="0.05"> + <argument index="3" name="max_slides" type="int" default="4"> </argument> - <argument index="4" name="max_slides" type="int" default="4"> + <argument index="4" name="floor_max_angle" type="float" default="0.785398"> </argument> - <argument index="5" name="floor_max_angle" type="float" default="0.785398"> + <argument index="5" name="infinite_inertia" type="bool" default="true"> </argument> <description> Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody[/code] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes. diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index 0448707c2e..fdc974630f 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -64,6 +64,10 @@ </argument> <argument index="1" name="infinite_inertia" type="bool" default="true"> </argument> + <argument index="2" name="exclude_raycast_shapes" type="bool" default="true"> + </argument> + <argument index="3" name="test_only" type="bool" default="false"> + </argument> <description> Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision2D], which contains information about the collision. </description> @@ -93,6 +97,26 @@ Returns the movement that remained when the body stopped. To get more detailed information about collisions that occurred, use [method get_slide_collision]. </description> </method> + <method name="move_and_slide_with_snap"> + <return type="Vector2"> + </return> + <argument index="0" name="linear_velocity" type="Vector2"> + </argument> + <argument index="1" name="snap" type="Vector2"> + </argument> + <argument index="2" name="floor_normal" type="Vector2" default="Vector2( 0, 0 )"> + </argument> + <argument index="3" name="infinite_inertia" type="bool" default="true"> + </argument> + <argument index="4" name="slope_stop_min_velocity" type="float" default="5"> + </argument> + <argument index="5" name="max_bounces" type="int" default="4"> + </argument> + <argument index="6" name="floor_max_angle" type="float" default="0.785398"> + </argument> + <description> + </description> + </method> <method name="test_move"> <return type="bool"> </return> @@ -111,6 +135,8 @@ <member name="collision/safe_margin" type="float" setter="set_safe_margin" getter="get_safe_margin"> If the body is at least this close to another body, this body will consider them to be colliding. </member> + <member name="motion/sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled"> + </member> </members> <constants> </constants> diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml index 8c2aa01f81..9454a16988 100644 --- a/doc/classes/MainLoop.xml +++ b/doc/classes/MainLoop.xml @@ -134,5 +134,7 @@ </constant> <constant name="NOTIFICATION_WM_ABOUT" value="91"> </constant> + <constant name="NOTIFICATION_CRASH" value="92"> + </constant> </constants> </class> diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index 83cadf959c..afb5d7db93 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -121,7 +121,7 @@ </constant> <constant name="ARRAY_FLAG_USE_16_BIT_BONES" value="524288" enum="ArrayFormat"> </constant> - <constant name="ARRAY_COMPRESS_DEFAULT" value="97792" enum="ArrayFormat"> + <constant name="ARRAY_COMPRESS_DEFAULT" value="97280" enum="ArrayFormat"> </constant> <constant name="ARRAY_VERTEX" value="0" enum="ArrayType"> </constant> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 8c114a55c9..10e7f1b172 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -71,6 +71,8 @@ </argument> <argument index="1" name="id" type="int" default="0"> </argument> + <argument index="2" name="mode" type="int" enum="NetworkedMultiplayerPeer.TransferMode" default="2"> + </argument> <description> Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method NetworkedMultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers. </description> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 8bae412053..bfef3c8588 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -223,8 +223,8 @@ </argument> <description> Fetches a node. The [NodePath] can be either a relative path (from the current node) or an absolute path (in the scene tree) to a node. If the path does not exist, a [code]null instance[/code] is returned and attempts to access it will result in an "Attempt to call <method> on a null instance." error. - Note: fetching absolute paths only works when the node is inside the scene tree (see [method is_inside_tree]). - [i]Example:[/i] Assume your current node is Character and the following tree: + [b]Note:[/b] Fetching absolute paths only works when the node is inside the scene tree (see [method is_inside_tree]). + [b]Example:[/b] Assume your current node is Character and the following tree: [codeblock] /root /root/Character @@ -733,6 +733,14 @@ Enables or disabled internal processing for this node. Internal processing happens in isolation from the normal [method _process] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or processing is disabled for scripting ([method set_process]). Only useful for advanced uses to manipulate built-in nodes behaviour. </description> </method> + <method name="set_process_priority"> + <return type="void"> + </return> + <argument index="0" name="priority" type="int"> + </argument> + <description> + </description> + </method> <method name="set_process_unhandled_input"> <return type="void"> </return> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 1526b1be8c..cf86176086 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -147,6 +147,12 @@ Returns the command line arguments passed to the engine. </description> </method> + <method name="get_connected_midi_inputs"> + <return type="PoolStringArray"> + </return> + <description> + </description> + </method> <method name="get_date" qualifiers="const"> <return type="Dictionary"> </return> diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml index de3a251a79..3b11d9e47f 100644 --- a/doc/classes/Performance.xml +++ b/doc/classes/Performance.xml @@ -106,7 +106,9 @@ <constant name="PHYSICS_3D_ISLAND_COUNT" value="26" enum="Monitor"> Number of islands in the 3D physics engine. </constant> - <constant name="MONITOR_MAX" value="27" enum="Monitor"> + <constant name="AUDIO_OUTPUT_LATENCY" value="27" enum="Monitor"> + </constant> + <constant name="MONITOR_MAX" value="28" enum="Monitor"> </constant> </constants> </class> diff --git a/doc/classes/Physics2DDirectBodyState.xml b/doc/classes/Physics2DDirectBodyState.xml index 73a2d792ab..52c89248ff 100644 --- a/doc/classes/Physics2DDirectBodyState.xml +++ b/doc/classes/Physics2DDirectBodyState.xml @@ -11,6 +11,58 @@ <demos> </demos> <methods> + <method name="add_central_force"> + <return type="void"> + </return> + <argument index="0" name="force" type="Vector2"> + </argument> + <description> + </description> + </method> + <method name="add_force"> + <return type="void"> + </return> + <argument index="0" name="offset" type="Vector2"> + </argument> + <argument index="1" name="force" type="Vector2"> + </argument> + <description> + </description> + </method> + <method name="add_torque"> + <return type="void"> + </return> + <argument index="0" name="torque" type="float"> + </argument> + <description> + </description> + </method> + <method name="apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="impulse" type="Vector2"> + </argument> + <description> + </description> + </method> + <method name="apply_impulse"> + <return type="void"> + </return> + <argument index="0" name="offset" type="Vector2"> + </argument> + <argument index="1" name="impulse" type="Vector2"> + </argument> + <description> + </description> + </method> + <method name="apply_torque_impulse"> + <return type="void"> + </return> + <argument index="0" name="impulse" type="float"> + </argument> + <description> + </description> + </method> <method name="get_contact_collider" qualifiers="const"> <return type="RID"> </return> diff --git a/doc/classes/Physics2DServer.xml b/doc/classes/Physics2DServer.xml index c302797704..a473de4ce8 100644 --- a/doc/classes/Physics2DServer.xml +++ b/doc/classes/Physics2DServer.xml @@ -287,6 +287,16 @@ Sets the transform matrix for an area. </description> </method> + <method name="body_add_central_force"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="force" type="Vector2"> + </argument> + <description> + </description> + </method> <method name="body_add_collision_exception"> <return type="void"> </return> @@ -324,6 +334,26 @@ Adds a shape to the body, along with a transform matrix. Shapes are usually referenced by their index, so you should track which shape has a given index. </description> </method> + <method name="body_add_torque"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="torque" type="float"> + </argument> + <description> + </description> + </method> + <method name="body_apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="impulse" type="Vector2"> + </argument> + <description> + </description> + </method> <method name="body_apply_impulse"> <return type="void"> </return> @@ -337,6 +367,16 @@ Adds a positioned impulse to the applied force and torque. Both the force and the offset from the body origin are in global coordinates. </description> </method> + <method name="body_apply_torque_impulse"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="impulse" type="float"> + </argument> + <description> + </description> + </method> <method name="body_attach_object_instance_id"> <return type="void"> </return> diff --git a/doc/classes/PhysicsDirectBodyState.xml b/doc/classes/PhysicsDirectBodyState.xml index 74190d0780..5117c9ef6c 100644 --- a/doc/classes/PhysicsDirectBodyState.xml +++ b/doc/classes/PhysicsDirectBodyState.xml @@ -35,6 +35,14 @@ <description> </description> </method> + <method name="apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="j" type="Vector3"> + </argument> + <description> + </description> + </method> <method name="apply_impulse"> <return type="void"> </return> @@ -107,6 +115,15 @@ <description> </description> </method> + <method name="get_contact_impulse" qualifiers="const"> + <return type="float"> + </return> + <argument index="0" name="contact_idx" type="int"> + </argument> + <description> + Impulse created by the contact. Only implemented for Bullet physics. + </description> + </method> <method name="get_contact_local_normal" qualifiers="const"> <return type="Vector3"> </return> diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml new file mode 100644 index 0000000000..5512c4605a --- /dev/null +++ b/doc/classes/PhysicsMaterial.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="PhysicsMaterial" inherits="Resource" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index d45a3adc9c..88a104cb11 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -294,6 +294,16 @@ Sets the transform matrix for an area. </description> </method> + <method name="body_add_central_force"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="force" type="Vector3"> + </argument> + <description> + </description> + </method> <method name="body_add_collision_exception"> <return type="void"> </return> @@ -305,6 +315,18 @@ Adds a body to the list of bodies exempt from collisions. </description> </method> + <method name="body_add_force"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="force" type="Vector3"> + </argument> + <argument index="2" name="position" type="Vector3"> + </argument> + <description> + </description> + </method> <method name="body_add_shape"> <return type="void"> </return> @@ -318,6 +340,26 @@ Adds a shape to the body, along with a transform matrix. Shapes are usually referenced by their index, so you should track which shape has a given index. </description> </method> + <method name="body_add_torque"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="torque" type="Vector3"> + </argument> + <description> + </description> + </method> + <method name="body_apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="body" type="RID"> + </argument> + <argument index="1" name="impulse" type="Vector3"> + </argument> + <description> + </description> + </method> <method name="body_apply_impulse"> <return type="void"> </return> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 83d1246e2a..0c8769714b 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -136,6 +136,8 @@ <method name="add_separator"> <return type="void"> </return> + <argument index="0" name="label" type="String" default=""""> + </argument> <description> Add a separator between items. Separators also occupy an index. </description> @@ -507,7 +509,7 @@ </member> <member name="hide_on_state_item_selection" type="bool" setter="set_hide_on_state_item_selection" getter="is_hide_on_state_item_selection"> </member> - <member name="submenu_popup_delay" type="real" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay"> + <member name="submenu_popup_delay" type="float" setter="set_submenu_popup_delay" getter="get_submenu_popup_delay"> Sets the delay time for the submenu item to popup on mouse hovering. If the popup menu is added as a child of another (acting as a submenu), it will inherit the delay time of the parent menu item. Default value: [code]0.3[/code] seconds. </member> </members> @@ -553,6 +555,10 @@ </theme_item> <theme_item name="hseparation" type="int"> </theme_item> + <theme_item name="labeled_separator_left" type="StyleBox"> + </theme_item> + <theme_item name="labeled_separator_right" type="StyleBox"> + </theme_item> <theme_item name="panel" type="StyleBox"> </theme_item> <theme_item name="panel_disabled" type="StyleBox"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 666f6b4710..a4af5509a6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -559,6 +559,8 @@ <member name="physics/2d/thread_model" type="int" setter="" getter=""> Set whether physics is run on the main thread or a separate one. Running the server on a thread increases performance, but restricts API Access to only physics process. </member> + <member name="physics/3d/active_soft_world" type="bool" setter="" getter=""> + </member> <member name="physics/3d/physics_engine" type="String" setter="" getter=""> </member> <member name="physics/common/physics_fps" type="int" setter="" getter=""> @@ -615,8 +617,6 @@ </member> <member name="rendering/quality/intended_usage/framebuffer_allocation.mobile" type="int" setter="" getter=""> </member> - <member name="rendering/quality/intended_usage/framebuffer_mode" type="int" setter="" getter=""> - </member> <member name="rendering/quality/reflections/high_quality_ggx" type="bool" setter="" getter=""> For reflection probes and panorama backgrounds (sky), use a high amount of samples to create ggx blurred versions (used for roughness). </member> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index 4253560f67..038464e127 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -24,6 +24,40 @@ Called during physics processing, allowing you to read and safely modify the simulation state for the object. By default it works in addition to the usual physics behavior, but [method set_use_custom_integrator] allows you to disable the default behavior and do fully custom force integration for a body. </description> </method> + <method name="add_central_force"> + <return type="void"> + </return> + <argument index="0" name="force" type="Vector3"> + </argument> + <description> + </description> + </method> + <method name="add_force"> + <return type="void"> + </return> + <argument index="0" name="force" type="Vector3"> + </argument> + <argument index="1" name="position" type="Vector3"> + </argument> + <description> + </description> + </method> + <method name="add_torque"> + <return type="void"> + </return> + <argument index="0" name="torque" type="Vector3"> + </argument> + <description> + </description> + </method> + <method name="apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="impulse" type="Vector3"> + </argument> + <description> + </description> + </method> <method name="apply_impulse"> <return type="void"> </return> @@ -117,6 +151,8 @@ <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="RigidBody.Mode"> The body mode from the MODE_* enum. Modes include: MODE_STATIC, MODE_KINEMATIC, MODE_RIGID, and MODE_CHARACTER. </member> + <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> + </member> <member name="sleeping" type="bool" setter="set_sleeping" getter="is_sleeping"> If [code]true[/code] RigidBody is sleeping and will not calculate forces until woken up by a collision or the [code]apply_impulse[/code] method. </member> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index 75fbb48454..2265c777c8 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -23,6 +23,14 @@ Allows you to read and safely modify the simulation state for the object. Use this instead of [Node._physics_process] if you need to directly change the body's [code]position[/code] or other physics properties. By default it works in addition to the usual physics behavior, but [member custom_integrator] allows you to disable the default behavior and write custom force integration for a body. </description> </method> + <method name="add_central_force"> + <return type="void"> + </return> + <argument index="0" name="force" type="Vector2"> + </argument> + <description> + </description> + </method> <method name="add_force"> <return type="void"> </return> @@ -34,6 +42,22 @@ Adds a positioned force to the body. Both the force and the offset from the body origin are in global coordinates. </description> </method> + <method name="add_torque"> + <return type="void"> + </return> + <argument index="0" name="torque" type="float"> + </argument> + <description> + </description> + </method> + <method name="apply_central_impulse"> + <return type="void"> + </return> + <argument index="0" name="impulse" type="Vector2"> + </argument> + <description> + </description> + </method> <method name="apply_impulse"> <return type="void"> </return> @@ -45,6 +69,14 @@ Applies a positioned impulse to the body (which will be affected by the body mass and shape). This is the equivalent of hitting a billiard ball with a cue: a force that is applied instantaneously. Both the impulse and the offset from the body origin are in global coordinates. </description> </method> + <method name="apply_torque_impulse"> + <return type="void"> + </return> + <argument index="0" name="torque" type="float"> + </argument> + <description> + </description> + </method> <method name="get_colliding_bodies" qualifiers="const"> <return type="Array"> </return> @@ -130,6 +162,8 @@ <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="RigidBody2D.Mode"> The body's mode. See [code]MODE_*[/code] constants. Default value: [code]MODE_RIGID[/code]. </member> + <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> + </member> <member name="sleeping" type="bool" setter="set_sleeping" getter="is_sleeping"> If [code]true[/code] the body is sleeping and will not calculate forces until woken up by a collision or by using [method apply_impulse] or [method add_force]. </member> diff --git a/doc/classes/RootMotionView.xml b/doc/classes/RootMotionView.xml new file mode 100644 index 0000000000..97008f05ff --- /dev/null +++ b/doc/classes/RootMotionView.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="RootMotionView" inherits="VisualInstance" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="animation_path" type="NodePath" setter="set_animation_path" getter="get_animation_path"> + </member> + <member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size"> + </member> + <member name="color" type="Color" setter="set_color" getter="get_color"> + </member> + <member name="radius" type="float" setter="set_radius" getter="get_radius"> + </member> + <member name="zero_y" type="bool" setter="set_zero_y" getter="get_zero_y"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index f5a19ede0c..c85bee9b84 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -384,20 +384,28 @@ Call a group only once even if the call is executed many times. </constant> <constant name="STRETCH_MODE_DISABLED" value="0" enum="StretchMode"> + No stretching. </constant> <constant name="STRETCH_MODE_2D" value="1" enum="StretchMode"> + Render stretching in higher resolution (interpolated). </constant> <constant name="STRETCH_MODE_VIEWPORT" value="2" enum="StretchMode"> + Keep the specified display resolution. No interpolation. Content may appear pixelated. </constant> <constant name="STRETCH_ASPECT_IGNORE" value="0" enum="StretchAspect"> + Fill the window with the content stretched to cover excessive space. Content may appear elongated. </constant> <constant name="STRETCH_ASPECT_KEEP" value="1" enum="StretchAspect"> + Retain the same aspect ratio by padding with black bars in either axes. No expansion of content. </constant> <constant name="STRETCH_ASPECT_KEEP_WIDTH" value="2" enum="StretchAspect"> + Expand vertically. Left/right black bars may appear if the window is too wide. </constant> <constant name="STRETCH_ASPECT_KEEP_HEIGHT" value="3" enum="StretchAspect"> + Expand horizontally. Top/bottom black bars may appear if the window is too tall. </constant> <constant name="STRETCH_ASPECT_EXPAND" value="4" enum="StretchAspect"> + Expand in both directions, retaining the same aspect ratio. No black bars. </constant> </constants> </class> diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml index f09d282026..a3ad3a778e 100644 --- a/doc/classes/ScriptCreateDialog.xml +++ b/doc/classes/ScriptCreateDialog.xml @@ -21,10 +21,8 @@ <return type="void"> </return> <argument index="0" name="inherits" type="String"> - The dialog's "Inherits" field content. </argument> <argument index="1" name="path" type="String"> - The dialog's "Path" field content. </argument> <description> Prefills required fields to configure the ScriptCreateDialog for use. diff --git a/doc/classes/Slider.xml b/doc/classes/Slider.xml index a0ab4fe878..1e6ed65b48 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -15,10 +15,10 @@ <members> <member name="editable" type="bool" setter="set_editable" getter="is_editable"> </member> - <member name="scrollable" type="bool" setter="set_scrollable" getter="is_scrollable"> - </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode"> </member> + <member name="scrollable" type="bool" setter="set_scrollable" getter="is_scrollable"> + </member> <member name="tick_count" type="int" setter="set_ticks" getter="get_ticks"> </member> <member name="ticks_on_borders" type="bool" setter="set_ticks_on_borders" getter="get_ticks_on_borders"> diff --git a/doc/classes/SoftBody.xml b/doc/classes/SoftBody.xml new file mode 100644 index 0000000000..c3c789a6de --- /dev/null +++ b/doc/classes/SoftBody.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="SoftBody" inherits="MeshInstance" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_collision_exception_with"> + <return type="void"> + </return> + <argument index="0" name="body" type="Node"> + </argument> + <description> + </description> + </method> + <method name="get_collision_layer_bit" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_collision_mask_bit" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <description> + </description> + </method> + <method name="is_ray_pickable" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> + <method name="remove_collision_exception_with"> + <return type="void"> + </return> + <argument index="0" name="body" type="Node"> + </argument> + <description> + </description> + </method> + <method name="set_collision_layer_bit"> + <return type="void"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <argument index="1" name="value" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_collision_mask_bit"> + <return type="void"> + </return> + <argument index="0" name="bit" type="int"> + </argument> + <argument index="1" name="value" type="bool"> + </argument> + <description> + </description> + </method> + <method name="set_ray_pickable"> + <return type="void"> + </return> + <argument index="0" name="ray_pickable" type="bool"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="areaAngular_stiffness" type="float" setter="set_areaAngular_stiffness" getter="get_areaAngular_stiffness"> + </member> + <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> + </member> + <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> + </member> + <member name="damping_coefficient" type="float" setter="set_damping_coefficient" getter="get_damping_coefficient"> + </member> + <member name="drag_coefficient" type="float" setter="set_drag_coefficient" getter="get_drag_coefficient"> + </member> + <member name="linear_stiffness" type="float" setter="set_linear_stiffness" getter="get_linear_stiffness"> + </member> + <member name="parent_collision_ignore" type="NodePath" setter="set_parent_collision_ignore" getter="get_parent_collision_ignore"> + </member> + <member name="pose_matching_coefficient" type="float" setter="set_pose_matching_coefficient" getter="get_pose_matching_coefficient"> + </member> + <member name="pressure_coefficient" type="float" setter="set_pressure_coefficient" getter="get_pressure_coefficient"> + </member> + <member name="simulation_precision" type="int" setter="set_simulation_precision" getter="get_simulation_precision"> + </member> + <member name="total_mass" type="float" setter="set_total_mass" getter="get_total_mass"> + </member> + <member name="volume_stiffness" type="float" setter="set_volume_stiffness" getter="get_volume_stiffness"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index d9242d8c42..ef1bcc30b3 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -69,6 +69,12 @@ Returns whether node notifies about its local transformation changes. Spatial will not propagate this by default. </description> </method> + <method name="is_scale_disabled" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> <method name="is_set_as_toplevel" qualifiers="const"> <return type="bool"> </return> @@ -190,6 +196,14 @@ Makes the node ignore its parents transformations. Node transformations are only in global space. </description> </method> + <method name="set_disable_scale"> + <return type="void"> + </return> + <argument index="0" name="disable" type="bool"> + </argument> + <description> + </description> + </method> <method name="set_identity"> <return type="void"> </return> diff --git a/doc/classes/SpatialMaterial.xml b/doc/classes/SpatialMaterial.xml index b45f2a13d4..4f48889531 100644 --- a/doc/classes/SpatialMaterial.xml +++ b/doc/classes/SpatialMaterial.xml @@ -84,8 +84,12 @@ </member> <member name="flags_albedo_tex_force_srgb" type="bool" setter="set_flag" getter="get_flag"> </member> + <member name="flags_disable_ambient_light" type="bool" setter="set_flag" getter="get_flag"> + </member> <member name="flags_do_not_receive_shadows" type="bool" setter="set_flag" getter="get_flag"> </member> + <member name="flags_ensure_correct_normals" type="bool" setter="set_flag" getter="get_flag"> + </member> <member name="flags_fixed_size" type="bool" setter="set_flag" getter="get_flag"> </member> <member name="flags_no_depth_test" type="bool" setter="set_flag" getter="get_flag"> @@ -116,6 +120,8 @@ </member> <member name="params_alpha_scissor_threshold" type="float" setter="set_alpha_scissor_threshold" getter="get_alpha_scissor_threshold"> </member> + <member name="params_billboard_keep_scale" type="bool" setter="set_flag" getter="get_flag"> + </member> <member name="params_billboard_mode" type="int" setter="set_billboard_mode" getter="get_billboard_mode" enum="SpatialMaterial.BillboardMode"> </member> <member name="params_blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="SpatialMaterial.BlendMode"> @@ -304,23 +310,29 @@ </constant> <constant name="FLAG_FIXED_SIZE" value="6" enum="Flags"> </constant> - <constant name="FLAG_UV1_USE_TRIPLANAR" value="7" enum="Flags"> + <constant name="FLAG_BILLBOARD_KEEP_SCALE" value="7" enum="Flags"> + </constant> + <constant name="FLAG_UV1_USE_TRIPLANAR" value="8" enum="Flags"> + </constant> + <constant name="FLAG_UV2_USE_TRIPLANAR" value="9" enum="Flags"> + </constant> + <constant name="FLAG_AO_ON_UV2" value="11" enum="Flags"> </constant> - <constant name="FLAG_UV2_USE_TRIPLANAR" value="8" enum="Flags"> + <constant name="FLAG_EMISSION_ON_UV2" value="12" enum="Flags"> </constant> - <constant name="FLAG_AO_ON_UV2" value="10" enum="Flags"> + <constant name="FLAG_USE_ALPHA_SCISSOR" value="13" enum="Flags"> </constant> - <constant name="FLAG_EMISSION_ON_UV2" value="11" enum="Flags"> + <constant name="FLAG_TRIPLANAR_USE_WORLD" value="10" enum="Flags"> </constant> - <constant name="FLAG_USE_ALPHA_SCISSOR" value="12" enum="Flags"> + <constant name="FLAG_ALBEDO_TEXTURE_FORCE_SRGB" value="14" enum="Flags"> </constant> - <constant name="FLAG_TRIPLANAR_USE_WORLD" value="9" enum="Flags"> + <constant name="FLAG_DONT_RECEIVE_SHADOWS" value="15" enum="Flags"> </constant> - <constant name="FLAG_ALBEDO_TEXTURE_FORCE_SRGB" value="13" enum="Flags"> + <constant name="FLAG_DISABLE_AMBIENT_LIGHT" value="17" enum="Flags"> </constant> - <constant name="FLAG_DONT_RECEIVE_SHADOWS" value="14" enum="Flags"> + <constant name="FLAG_ENSURE_CORRECT_NORMALS" value="16" enum="Flags"> </constant> - <constant name="FLAG_MAX" value="15" enum="Flags"> + <constant name="FLAG_MAX" value="18" enum="Flags"> </constant> <constant name="DIFFUSE_BURLEY" value="0" enum="DiffuseMode"> </constant> diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index 68373ebc4f..0e11d797e4 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -58,6 +58,13 @@ If [code]true[/code] the given animation will loop. </description> </method> + <method name="get_animation_names" qualifiers="const"> + <return type="PoolStringArray"> + </return> + <description> + Returns an array containing the names associated to each animation. Values are placed in alphabetical order. + </description> + </method> <method name="get_animation_speed" qualifiers="const"> <return type="float"> </return> @@ -127,13 +134,6 @@ Changes the animation's name to [code]newname[/code]. </description> </method> - <method name="get_animation_names"> - <return type="PoolStringArray"> - </return> - <description> - Returns an array containing the names associated to each animation. Values are placed in alphabetical order. - </description> - </method> <method name="set_animation_loop"> <return type="void"> </return> diff --git a/doc/classes/StaticBody.xml b/doc/classes/StaticBody.xml index 26b4857670..442a520e30 100644 --- a/doc/classes/StaticBody.xml +++ b/doc/classes/StaticBody.xml @@ -27,6 +27,8 @@ <member name="friction" type="float" setter="set_friction" getter="get_friction"> The body friction, from 0 (frictionless) to 1 (full friction). </member> + <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> + </member> </members> <constants> </constants> diff --git a/doc/classes/StaticBody2D.xml b/doc/classes/StaticBody2D.xml index 95acda79df..917773d502 100644 --- a/doc/classes/StaticBody2D.xml +++ b/doc/classes/StaticBody2D.xml @@ -26,6 +26,8 @@ <member name="friction" type="float" setter="set_friction" getter="get_friction"> The body's friction. Values range from [code]0[/code] (no friction) to [code]1[/code] (full friction). </member> + <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> + </member> </members> <constants> </constants> diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml index 3081abd5c4..cf8769d22b 100644 --- a/doc/classes/StreamPeerSSL.xml +++ b/doc/classes/StreamPeerSSL.xml @@ -15,7 +15,7 @@ <method name="accept_stream"> <return type="int" enum="Error"> </return> - <argument index="0" name="stream" type="StreamPeer"> + <argument index="0" name="base" type="StreamPeer"> </argument> <description> </description> @@ -55,17 +55,20 @@ </description> </method> </methods> + <members> + <member name="blocking_handshake" type="bool" setter="set_blocking_handshake_enabled" getter="is_blocking_handshake_enabled"> + </member> + </members> <constants> <constant name="STATUS_DISCONNECTED" value="0" enum="Status"> A status representing a [code]StreamPeerSSL[/code] that is disconnected. </constant> - <constant name="STATUS_CONNECTED" value="1" enum="Status"> + <constant name="STATUS_CONNECTED" value="2" enum="Status"> A status representing a [code]StreamPeerSSL[/code] that is connected to a host. </constant> - <constant name="STATUS_ERROR_NO_CERTIFICATE" value="2" enum="Status"> - An errot status that shows the peer did not present a SSL certificate and validation was requested. + <constant name="STATUS_ERROR" value="3" enum="Status"> </constant> - <constant name="STATUS_ERROR_HOSTNAME_MISMATCH" value="3" enum="Status"> + <constant name="STATUS_ERROR_HOSTNAME_MISMATCH" value="4" enum="Status"> An error status that shows a mismatch in the SSL certificate domain presented by the host and the domain requested for validation. </constant> </constants> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 0ba1066dfd..a42f508b59 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -653,7 +653,8 @@ <argument index="2" name="maxsplit" type="int" default="0"> </argument> <description> - Splits the string by a [code]divisor[/code] string and returns an array of the substrings, starting from right. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + Splits the string by a [code]divisor[/code] string and returns an array of the substrings, starting from right. + [b]Example:[/b] "One,Two,Three" will return ["One","Two","Three"] if split by ",". If [code]maxsplit[/code] is specified, then it is number of splits to do, default is 0 which splits all the items. </description> </method> @@ -698,7 +699,8 @@ <argument index="2" name="maxsplit" type="int" default="0"> </argument> <description> - Splits the string by a divisor string and returns an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + Splits the string by a divisor string and returns an array of the substrings. + [b]Example:[/b] "One,Two,Three" will return ["One","Two","Three"] if split by ",". If [code]maxsplit[/code] is given, at most maxsplit number of splits occur, and the remainder of the string is returned as the final element of the list (thus, the list will have at most maxsplit+1 elements) </description> </method> @@ -710,7 +712,8 @@ <argument index="1" name="allow_empty" type="bool" default="True"> </argument> <description> - Splits the string in floats by using a divisor string and returns an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". + Splits the string in floats by using a divisor string and returns an array of the substrings. + [b]Example:[/b] "1,2.5,3" will return [1,2.5,3] if split by ",". </description> </method> <method name="strip_edges"> diff --git a/doc/classes/StyleBoxLine.xml b/doc/classes/StyleBoxLine.xml index c891c8cf83..146b4b0a3d 100644 --- a/doc/classes/StyleBoxLine.xml +++ b/doc/classes/StyleBoxLine.xml @@ -13,7 +13,9 @@ <members> <member name="color" type="Color" setter="set_color" getter="get_color"> </member> - <member name="grow" type="float" setter="set_grow" getter="get_grow"> + <member name="grow_begin" type="float" setter="set_grow_begin" getter="get_grow_begin"> + </member> + <member name="grow_end" type="float" setter="set_grow_end" getter="get_grow_end"> </member> <member name="thickness" type="int" setter="set_thickness" getter="get_thickness"> </member> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 7d78d71330..deda7bc292 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -170,7 +170,7 @@ </return> <argument index="0" name="existing" type="ArrayMesh" default="null"> </argument> - <argument index="1" name="flags" type="int" default="97792"> + <argument index="1" name="flags" type="int" default="97280"> </argument> <description> Returns a constructed [ArrayMesh] from current information passed in. If an existing [ArrayMesh] is passed in as an argument, will add an extra surface to the existing [ArrayMesh]. diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index 11e94abc03..5acfd6194e 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TabContainer" inherits="Control" category="Core" version="3.1"> +<class name="TabContainer" inherits="Container" category="Core" version="3.1"> <brief_description> Tabbed Container. </brief_description> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 36408663f6..a958c3fcfa 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -227,12 +227,6 @@ Insert a given text at the cursor position. </description> </method> - <method name="is_breakpoint_gutter_enabled" qualifiers="const"> - <return type="bool"> - </return> - <description> - </description> - </method> <method name="is_folded" qualifiers="const"> <return type="bool"> </return> @@ -322,14 +316,6 @@ Select all the text. </description> </method> - <method name="set_breakpoint_gutter_enabled"> - <return type="void"> - </return> - <argument index="0" name="enable" type="bool"> - </argument> - <description> - </description> - </method> <method name="set_line_as_hidden"> <return type="void"> </return> @@ -541,6 +527,8 @@ </theme_item> <theme_item name="read_only" type="StyleBox"> </theme_item> + <theme_item name="safe_line_number_color" type="Color"> + </theme_item> <theme_item name="selection_color" type="Color"> </theme_item> <theme_item name="symbol_color" type="Color"> diff --git a/doc/classes/TextFile.xml b/doc/classes/TextFile.xml new file mode 100644 index 0000000000..f8c1fd690e --- /dev/null +++ b/doc/classes/TextFile.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="TextFile" inherits="Resource" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml index 2ae4ef2cf8..f74420e8b1 100644 --- a/doc/classes/TextureProgress.xml +++ b/doc/classes/TextureProgress.xml @@ -77,5 +77,11 @@ <constant name="FILL_COUNTER_CLOCKWISE" value="5" enum="FillMode"> Turns the node into a radial bar. The [member texture_progress] fills counter-clockwise. See [member radial_center_offset], [member radial_initial_angle] and [member radial_fill_degrees] to refine its behavior. </constant> + <constant name="FILL_BILINEAR_LEFT_AND_RIGHT" value="6" enum="FillMode"> + </constant> + <constant name="FILL_BILINEAR_TOP_AND_BOTTOM" value="7" enum="FillMode"> + </constant> + <constant name="FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE" value="8" enum="FillMode"> + </constant> </constants> </class> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 3486b721ca..73d60e49b7 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -68,7 +68,7 @@ <return type="Array"> </return> <description> - Returns an array of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). + Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). </description> </method> <method name="get_used_cells_by_id" qualifiers="const"> @@ -157,6 +157,16 @@ If you need these to be immediately updated, you can call [method update_dirty_quadrants]. </description> </method> + <method name="set_celld"> + <return type="void"> + </return> + <argument index="0" name="position" type="Vector2"> + </argument> + <argument index="1" name="data" type="Dictionary"> + </argument> + <description> + </description> + </method> <method name="set_cellv"> <return type="void"> </return> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index bdf8634a6c..7121bc8b9c 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -136,6 +136,14 @@ Return the material of the tile. </description> </method> + <method name="tile_get_modulate" qualifiers="const"> + <return type="Color"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <description> + </description> + </method> <method name="tile_get_name" qualifiers="const"> <return type="String"> </return> @@ -292,6 +300,16 @@ Set the material of the tile. </description> </method> + <method name="tile_set_modulate"> + <return type="void"> + </return> + <argument index="0" name="id" type="int"> + </argument> + <argument index="1" name="color" type="Color"> + </argument> + <description> + </description> + </method> <method name="tile_set_name"> <return type="void"> </return> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index d82694d328..2332c1a7aa 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -181,12 +181,6 @@ Returns [code]true[/code] if any tweens are currently running. Note that this method doesn't consider tweens that have ended. </description> </method> - <method name="is_stopped" qualifiers="const"> - <return type="bool"> - </return> - <description> - </description> - </method> <method name="remove"> <return type="bool"> </return> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index a1a6d7f465..b9550c17fb 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -117,13 +117,13 @@ </description> </method> <method name="redo"> - <return type="void"> + <return type="bool"> </return> <description> </description> </method> <method name="undo"> - <return type="void"> + <return type="bool"> </return> <description> </description> diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index 893db84640..c4ebf7c96f 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -973,8 +973,9 @@ </return> <argument index="0" name="swap_buffers" type="bool" default="true"> </argument> + <argument index="1" name="frame_step" type="float" default="0.0"> + </argument> <description> - Draws a frame. </description> </method> <method name="environment_create"> @@ -1206,13 +1207,15 @@ </argument> <argument index="7" name="light_affect" type="float"> </argument> - <argument index="8" name="color" type="Color"> + <argument index="8" name="ao_channel_affect" type="float"> + </argument> + <argument index="9" name="color" type="Color"> </argument> - <argument index="9" name="quality" type="int" enum="VisualServer.EnvironmentSSAOQuality"> + <argument index="10" name="quality" type="int" enum="VisualServer.EnvironmentSSAOQuality"> </argument> - <argument index="10" name="blur" type="int" enum="VisualServer.EnvironmentSSAOBlur"> + <argument index="11" name="blur" type="int" enum="VisualServer.EnvironmentSSAOBlur"> </argument> - <argument index="11" name="bilateral_sharpness" type="float"> + <argument index="12" name="bilateral_sharpness" type="float"> </argument> <description> </description> @@ -1273,8 +1276,9 @@ </return> <argument index="0" name="swap_buffers" type="bool" default="true"> </argument> + <argument index="1" name="frame_step" type="float" default="0.0"> + </argument> <description> - Draws a frame. Same as [method draw]. </description> </method> <method name="force_sync"> @@ -2258,7 +2262,7 @@ </argument> <argument index="3" name="blend_shapes" type="Array" default="[ ]"> </argument> - <argument index="4" name="compress_format" type="int" default="97792"> + <argument index="4" name="compress_format" type="int" default="97280"> </argument> <description> Adds a surface generated from the Arrays to a mesh. See PRIMITIVE_TYPE_* constants for types. @@ -2437,6 +2441,32 @@ Returns the format of a mesh's surface. </description> </method> + <method name="mesh_surface_get_format_offset" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="format" type="int"> + </argument> + <argument index="1" name="vertex_len" type="int"> + </argument> + <argument index="2" name="index_len" type="int"> + </argument> + <argument index="3" name="array_index" type="int"> + </argument> + <description> + </description> + </method> + <method name="mesh_surface_get_format_stride" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="format" type="int"> + </argument> + <argument index="1" name="vertex_len" type="int"> + </argument> + <argument index="2" name="index_len" type="int"> + </argument> + <description> + </description> + </method> <method name="mesh_surface_get_index_array" qualifiers="const"> <return type="PoolByteArray"> </return> @@ -2494,6 +2524,20 @@ Sets a mesh's surface's material. </description> </method> + <method name="mesh_surface_update_region"> + <return type="void"> + </return> + <argument index="0" name="mesh" type="RID"> + </argument> + <argument index="1" name="surface" type="int"> + </argument> + <argument index="2" name="offset" type="int"> + </argument> + <argument index="3" name="data" type="PoolByteArray"> + </argument> + <description> + </description> + </method> <method name="multimesh_allocate"> <return type="void"> </return> @@ -2505,6 +2549,8 @@ </argument> <argument index="3" name="color_format" type="int" enum="VisualServer.MultimeshColorFormat"> </argument> + <argument index="4" name="custom_data_format" type="int" enum="VisualServer.MultimeshCustomDataFormat" default="0"> + </argument> <description> </description> </method> @@ -2550,6 +2596,16 @@ <description> </description> </method> + <method name="multimesh_instance_get_custom_data" qualifiers="const"> + <return type="Color"> + </return> + <argument index="0" name="multimesh" type="RID"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <description> + </description> + </method> <method name="multimesh_instance_get_transform" qualifiers="const"> <return type="Transform"> </return> @@ -2582,6 +2638,18 @@ <description> </description> </method> + <method name="multimesh_instance_set_custom_data"> + <return type="void"> + </return> + <argument index="0" name="multimesh" type="RID"> + </argument> + <argument index="1" name="index" type="int"> + </argument> + <argument index="2" name="custom_data" type="Color"> + </argument> + <description> + </description> + </method> <method name="multimesh_instance_set_transform"> <return type="void"> </return> @@ -2606,6 +2674,16 @@ <description> </description> </method> + <method name="multimesh_set_as_bulk_array"> + <return type="void"> + </return> + <argument index="0" name="multimesh" type="RID"> + </argument> + <argument index="1" name="array" type="PoolRealArray"> + </argument> + <description> + </description> + </method> <method name="multimesh_set_mesh"> <return type="void"> </return> @@ -3746,7 +3824,11 @@ </method> </methods> <signals> - <signal name="frame_drawn_in_thread"> + <signal name="frame_post_draw"> + <description> + </description> + </signal> + <signal name="frame_pre_draw"> <description> </description> </signal> @@ -3919,7 +4001,7 @@ <constant name="ARRAY_FLAG_USE_16_BIT_BONES" value="524288" enum="ArrayFormat"> Flag used to mark that the array uses 16 bit bones instead of 8 bit. </constant> - <constant name="ARRAY_COMPRESS_DEFAULT" value="97792" enum="ArrayFormat"> + <constant name="ARRAY_COMPRESS_DEFAULT" value="97280" enum="ArrayFormat"> Used to set flags ARRAY_COMPRESS_VERTEX, ARRAY_COMPRESS_NORMAL, ARRAY_COMPRESS_TANGENT, ARRAY_COMPRESS_COLOR, ARRAY_COMPRESS_TEX_UV, ARRAY_COMPRESS_TEX_UV2 and ARRAY_COMPRESS_WEIGHTS quickly. </constant> <constant name="PRIMITIVE_POINTS" value="0" enum="PrimitiveType"> @@ -4126,7 +4208,9 @@ </constant> <constant name="INSTANCE_FLAG_USE_BAKED_LIGHT" value="0" enum="InstanceFlags"> </constant> - <constant name="INSTANCE_FLAG_MAX" value="1" enum="InstanceFlags"> + <constant name="INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE" value="1" enum="InstanceFlags"> + </constant> + <constant name="INSTANCE_FLAG_MAX" value="2" enum="InstanceFlags"> </constant> <constant name="SHADOW_CASTING_SETTING_OFF" value="0" enum="ShadowCastingSetting"> </constant> diff --git a/doc/classes/VisualShader.xml b/doc/classes/VisualShader.xml new file mode 100644 index 0000000000..ab00080fef --- /dev/null +++ b/doc/classes/VisualShader.xml @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShader" inherits="Shader" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="add_node"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="node" type="VisualShaderNode"> + </argument> + <argument index="2" name="position" type="Vector2"> + </argument> + <argument index="3" name="id" type="int"> + </argument> + <description> + </description> + </method> + <method name="can_connect_nodes" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="from_node" type="int"> + </argument> + <argument index="2" name="from_port" type="int"> + </argument> + <argument index="3" name="to_node" type="int"> + </argument> + <argument index="4" name="to_port" type="int"> + </argument> + <description> + </description> + </method> + <method name="connect_nodes"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="from_node" type="int"> + </argument> + <argument index="2" name="from_port" type="int"> + </argument> + <argument index="3" name="to_node" type="int"> + </argument> + <argument index="4" name="to_port" type="int"> + </argument> + <description> + </description> + </method> + <method name="disconnect_nodes"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="from_node" type="int"> + </argument> + <argument index="2" name="from_port" type="int"> + </argument> + <argument index="3" name="to_node" type="int"> + </argument> + <argument index="4" name="to_port" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_node" qualifiers="const"> + <return type="VisualShaderNode"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="id" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_node_connections" qualifiers="const"> + <return type="Array"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <description> + </description> + </method> + <method name="get_node_list" qualifiers="const"> + <return type="PoolIntArray"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <description> + </description> + </method> + <method name="get_node_position" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="id" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_valid_node_id" qualifiers="const"> + <return type="int"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <description> + </description> + </method> + <method name="is_node_connection" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="from_node" type="int"> + </argument> + <argument index="2" name="from_port" type="int"> + </argument> + <argument index="3" name="to_node" type="int"> + </argument> + <argument index="4" name="to_port" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_node"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="id" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_mode"> + <return type="void"> + </return> + <argument index="0" name="mode" type="int" enum="Shader.Mode"> + </argument> + <description> + </description> + </method> + <method name="set_node_position"> + <return type="void"> + </return> + <argument index="0" name="type" type="int" enum="VisualShader.Type"> + </argument> + <argument index="1" name="id" type="int"> + </argument> + <argument index="2" name="position" type="Vector2"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="graph_offset" type="Vector2" setter="set_graph_offset" getter="get_graph_offset"> + </member> + </members> + <constants> + <constant name="TYPE_VERTEX" value="0" enum="Type"> + </constant> + <constant name="TYPE_FRAGMENT" value="1" enum="Type"> + </constant> + <constant name="TYPE_LIGHT" value="2" enum="Type"> + </constant> + <constant name="TYPE_MAX" value="3" enum="Type"> + </constant> + <constant name="NODE_ID_INVALID" value="-1"> + </constant> + <constant name="NODE_ID_OUTPUT" value="0"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml new file mode 100644 index 0000000000..ffcaf85eea --- /dev/null +++ b/doc/classes/VisualShaderNode.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNode" inherits="Resource" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + <method name="get_input_port_default_value" qualifiers="const"> + <return type="Variant"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_input_port_default_value"> + <return type="void"> + </return> + <argument index="0" name="port" type="int"> + </argument> + <argument index="1" name="value" type="Variant"> + </argument> + <description> + </description> + </method> + </methods> + <members> + <member name="default_input_values" type="Array" setter="_set_default_input_values" getter="_get_default_input_values"> + </member> + <member name="output_port_for_preview" type="int" setter="set_output_port_for_preview" getter="get_output_port_for_preview"> + </member> + </members> + <signals> + <signal name="editor_refresh_request"> + <description> + </description> + </signal> + </signals> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeColorConstant.xml b/doc/classes/VisualShaderNodeColorConstant.xml new file mode 100644 index 0000000000..f1079e0056 --- /dev/null +++ b/doc/classes/VisualShaderNodeColorConstant.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeColorConstant" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="constant" type="Color" setter="set_constant" getter="get_constant"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeColorOp.xml b/doc/classes/VisualShaderNodeColorOp.xml new file mode 100644 index 0000000000..d23c674503 --- /dev/null +++ b/doc/classes/VisualShaderNodeColorOp.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeColorOp" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="VisualShaderNodeColorOp.Operator"> + </member> + </members> + <constants> + <constant name="OP_SCREEN" value="0" enum="Operator"> + </constant> + <constant name="OP_DIFFERENCE" value="1" enum="Operator"> + </constant> + <constant name="OP_DARKEN" value="2" enum="Operator"> + </constant> + <constant name="OP_LIGHTEN" value="3" enum="Operator"> + </constant> + <constant name="OP_OVERLAY" value="4" enum="Operator"> + </constant> + <constant name="OP_DODGE" value="5" enum="Operator"> + </constant> + <constant name="OP_BURN" value="6" enum="Operator"> + </constant> + <constant name="OP_SOFT_LIGHT" value="7" enum="Operator"> + </constant> + <constant name="OP_HARD_LIGHT" value="8" enum="Operator"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeColorUniform.xml b/doc/classes/VisualShaderNodeColorUniform.xml new file mode 100644 index 0000000000..0b97c104a2 --- /dev/null +++ b/doc/classes/VisualShaderNodeColorUniform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeColorUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeCubeMap.xml b/doc/classes/VisualShaderNodeCubeMap.xml new file mode 100644 index 0000000000..9c56a23b7f --- /dev/null +++ b/doc/classes/VisualShaderNodeCubeMap.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeCubeMap" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="cube_map" type="CubeMap" setter="set_cube_map" getter="get_cube_map"> + </member> + <member name="texture_type" type="int" setter="set_texture_type" getter="get_texture_type" enum="VisualShaderNodeCubeMap.TextureType"> + </member> + </members> + <constants> + <constant name="TYPE_DATA" value="0" enum="TextureType"> + </constant> + <constant name="TYPE_COLOR" value="1" enum="TextureType"> + </constant> + <constant name="TYPE_NORMALMAP" value="2" enum="TextureType"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeCubeMapUniform.xml b/doc/classes/VisualShaderNodeCubeMapUniform.xml new file mode 100644 index 0000000000..fc95e4ddd9 --- /dev/null +++ b/doc/classes/VisualShaderNodeCubeMapUniform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeCubeMapUniform" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeDotProduct.xml b/doc/classes/VisualShaderNodeDotProduct.xml new file mode 100644 index 0000000000..c82f4cec3b --- /dev/null +++ b/doc/classes/VisualShaderNodeDotProduct.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeDotProduct" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeInput.xml b/doc/classes/VisualShaderNodeInput.xml new file mode 100644 index 0000000000..29de7f81d1 --- /dev/null +++ b/doc/classes/VisualShaderNodeInput.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeInput" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="input_name" type="String" setter="set_input_name" getter="get_input_name"> + </member> + </members> + <signals> + <signal name="input_type_changed"> + <description> + </description> + </signal> + </signals> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeOutput.xml b/doc/classes/VisualShaderNodeOutput.xml new file mode 100644 index 0000000000..7b65c6fd5e --- /dev/null +++ b/doc/classes/VisualShaderNodeOutput.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeOutput" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarConstant.xml b/doc/classes/VisualShaderNodeScalarConstant.xml new file mode 100644 index 0000000000..424fb2b3bd --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarConstant.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarConstant" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="constant" type="float" setter="set_constant" getter="get_constant"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarFunc.xml b/doc/classes/VisualShaderNodeScalarFunc.xml new file mode 100644 index 0000000000..7362ededce --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarFunc.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarFunc" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeScalarFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_SIN" value="0" enum="Function"> + </constant> + <constant name="FUNC_COS" value="1" enum="Function"> + </constant> + <constant name="FUNC_TAN" value="2" enum="Function"> + </constant> + <constant name="FUNC_ASIN" value="3" enum="Function"> + </constant> + <constant name="FUNC_ACOS" value="4" enum="Function"> + </constant> + <constant name="FUNC_ATAN" value="5" enum="Function"> + </constant> + <constant name="FUNC_SINH" value="6" enum="Function"> + </constant> + <constant name="FUNC_COSH" value="7" enum="Function"> + </constant> + <constant name="FUNC_TANH" value="8" enum="Function"> + </constant> + <constant name="FUNC_LOG" value="9" enum="Function"> + </constant> + <constant name="FUNC_EXP" value="10" enum="Function"> + </constant> + <constant name="FUNC_SQRT" value="11" enum="Function"> + </constant> + <constant name="FUNC_ABS" value="12" enum="Function"> + </constant> + <constant name="FUNC_SIGN" value="13" enum="Function"> + </constant> + <constant name="FUNC_FLOOR" value="14" enum="Function"> + </constant> + <constant name="FUNC_ROUND" value="15" enum="Function"> + </constant> + <constant name="FUNC_CEIL" value="16" enum="Function"> + </constant> + <constant name="FUNC_FRAC" value="17" enum="Function"> + </constant> + <constant name="FUNC_SATURATE" value="18" enum="Function"> + </constant> + <constant name="FUNC_NEGATE" value="19" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarInterp.xml b/doc/classes/VisualShaderNodeScalarInterp.xml new file mode 100644 index 0000000000..8420b34dd3 --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarInterp.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarInterp" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarOp.xml b/doc/classes/VisualShaderNodeScalarOp.xml new file mode 100644 index 0000000000..0ab0a46d0a --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarOp.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarOp" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="VisualShaderNodeScalarOp.Operator"> + </member> + </members> + <constants> + <constant name="OP_ADD" value="0" enum="Operator"> + </constant> + <constant name="OP_SUB" value="1" enum="Operator"> + </constant> + <constant name="OP_MUL" value="2" enum="Operator"> + </constant> + <constant name="OP_DIV" value="3" enum="Operator"> + </constant> + <constant name="OP_MOD" value="4" enum="Operator"> + </constant> + <constant name="OP_POW" value="5" enum="Operator"> + </constant> + <constant name="OP_MAX" value="6" enum="Operator"> + </constant> + <constant name="OP_MIN" value="7" enum="Operator"> + </constant> + <constant name="OP_ATAN2" value="8" enum="Operator"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeScalarUniform.xml b/doc/classes/VisualShaderNodeScalarUniform.xml new file mode 100644 index 0000000000..352355b475 --- /dev/null +++ b/doc/classes/VisualShaderNodeScalarUniform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeScalarUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTexture.xml b/doc/classes/VisualShaderNodeTexture.xml new file mode 100644 index 0000000000..18b2fc3b3f --- /dev/null +++ b/doc/classes/VisualShaderNodeTexture.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTexture" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="source" type="int" setter="set_source" getter="get_source" enum="VisualShaderNodeTexture.Source"> + </member> + <member name="texture" type="Texture" setter="set_texture" getter="get_texture"> + </member> + <member name="texture_type" type="int" setter="set_texture_type" getter="get_texture_type" enum="VisualShaderNodeTexture.TextureType"> + </member> + </members> + <constants> + <constant name="SOURCE_TEXTURE" value="0" enum="Source"> + </constant> + <constant name="SOURCE_SCREEN" value="1" enum="Source"> + </constant> + <constant name="SOURCE_2D_TEXTURE" value="2" enum="Source"> + </constant> + <constant name="SOURCE_2D_NORMAL" value="3" enum="Source"> + </constant> + <constant name="TYPE_DATA" value="0" enum="TextureType"> + </constant> + <constant name="TYPE_COLOR" value="1" enum="TextureType"> + </constant> + <constant name="TYPE_NORMALMAP" value="2" enum="TextureType"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTextureUniform.xml b/doc/classes/VisualShaderNodeTextureUniform.xml new file mode 100644 index 0000000000..1b5df52060 --- /dev/null +++ b/doc/classes/VisualShaderNodeTextureUniform.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTextureUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="color_default" type="int" setter="set_color_default" getter="get_color_default" enum="VisualShaderNodeTextureUniform.ColorDefault"> + </member> + <member name="texture_type" type="int" setter="set_texture_type" getter="get_texture_type" enum="VisualShaderNodeTextureUniform.TextureType"> + </member> + </members> + <constants> + <constant name="TYPE_DATA" value="0" enum="TextureType"> + </constant> + <constant name="TYPE_COLOR" value="1" enum="TextureType"> + </constant> + <constant name="TYPE_NORMALMAP" value="2" enum="TextureType"> + </constant> + <constant name="TYPE_ANISO" value="3" enum="TextureType"> + </constant> + <constant name="COLOR_DEFAULT_WHITE" value="0" enum="ColorDefault"> + </constant> + <constant name="COLOR_DEFAULT_BLACK" value="1" enum="ColorDefault"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformCompose.xml b/doc/classes/VisualShaderNodeTransformCompose.xml new file mode 100644 index 0000000000..e1bda001e3 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformCompose.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformCompose" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformConstant.xml b/doc/classes/VisualShaderNodeTransformConstant.xml new file mode 100644 index 0000000000..e86444563c --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformConstant.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformConstant" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="constant" type="Transform" setter="set_constant" getter="get_constant"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformDecompose.xml b/doc/classes/VisualShaderNodeTransformDecompose.xml new file mode 100644 index 0000000000..ee6d5c5d74 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformDecompose.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformDecompose" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformMult.xml b/doc/classes/VisualShaderNodeTransformMult.xml new file mode 100644 index 0000000000..31aa40db96 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformMult.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformMult" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="VisualShaderNodeTransformMult.Operator"> + </member> + </members> + <constants> + <constant name="OP_AxB" value="0" enum="Operator"> + </constant> + <constant name="OP_BxA" value="1" enum="Operator"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformUniform.xml b/doc/classes/VisualShaderNodeTransformUniform.xml new file mode 100644 index 0000000000..65b1088247 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformUniform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformUniform" inherits="VisualShaderNodeUniform" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeTransformVecMult.xml b/doc/classes/VisualShaderNodeTransformVecMult.xml new file mode 100644 index 0000000000..d3fb445717 --- /dev/null +++ b/doc/classes/VisualShaderNodeTransformVecMult.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeTransformVecMult" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="VisualShaderNodeTransformVecMult.Operator"> + </member> + </members> + <constants> + <constant name="OP_AxB" value="0" enum="Operator"> + </constant> + <constant name="OP_BxA" value="1" enum="Operator"> + </constant> + <constant name="OP_3x3_AxB" value="2" enum="Operator"> + </constant> + <constant name="OP_3x3_BxA" value="3" enum="Operator"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeUniform.xml b/doc/classes/VisualShaderNodeUniform.xml new file mode 100644 index 0000000000..2c02afd0a7 --- /dev/null +++ b/doc/classes/VisualShaderNodeUniform.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeUniform" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="uniform_name" type="String" setter="set_uniform_name" getter="get_uniform_name"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVec3Constant.xml b/doc/classes/VisualShaderNodeVec3Constant.xml new file mode 100644 index 0000000000..7492f56223 --- /dev/null +++ b/doc/classes/VisualShaderNodeVec3Constant.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVec3Constant" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="constant" type="Vector3" setter="set_constant" getter="get_constant"> + </member> + </members> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVec3Uniform.xml b/doc/classes/VisualShaderNodeVec3Uniform.xml new file mode 100644 index 0000000000..79a21680fb --- /dev/null +++ b/doc/classes/VisualShaderNodeVec3Uniform.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVec3Uniform" inherits="VisualShaderNodeUniform" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorCompose.xml b/doc/classes/VisualShaderNodeVectorCompose.xml new file mode 100644 index 0000000000..39798a82d1 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorCompose.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorCompose" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorDecompose.xml b/doc/classes/VisualShaderNodeVectorDecompose.xml new file mode 100644 index 0000000000..8df12d8ba9 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorDecompose.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorDecompose" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorFunc.xml b/doc/classes/VisualShaderNodeVectorFunc.xml new file mode 100644 index 0000000000..850b089645 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorFunc.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorFunc" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="function" type="int" setter="set_function" getter="get_function" enum="VisualShaderNodeVectorFunc.Function"> + </member> + </members> + <constants> + <constant name="FUNC_NORMALIZE" value="0" enum="Function"> + </constant> + <constant name="FUNC_SATURATE" value="1" enum="Function"> + </constant> + <constant name="FUNC_NEGATE" value="2" enum="Function"> + </constant> + <constant name="FUNC_RECIPROCAL" value="3" enum="Function"> + </constant> + <constant name="FUNC_RGB2HSV" value="4" enum="Function"> + </constant> + <constant name="FUNC_HSV2RGB" value="5" enum="Function"> + </constant> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorInterp.xml b/doc/classes/VisualShaderNodeVectorInterp.xml new file mode 100644 index 0000000000..909cd7eacc --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorInterp.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorInterp" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorLen.xml b/doc/classes/VisualShaderNodeVectorLen.xml new file mode 100644 index 0000000000..ad87d0bd0a --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorLen.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorLen" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/doc/classes/VisualShaderNodeVectorOp.xml b/doc/classes/VisualShaderNodeVectorOp.xml new file mode 100644 index 0000000000..3e20168f15 --- /dev/null +++ b/doc/classes/VisualShaderNodeVectorOp.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualShaderNodeVectorOp" inherits="VisualShaderNode" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <members> + <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="VisualShaderNodeVectorOp.Operator"> + </member> + </members> + <constants> + <constant name="OP_ADD" value="0" enum="Operator"> + </constant> + <constant name="OP_SUB" value="1" enum="Operator"> + </constant> + <constant name="OP_MUL" value="2" enum="Operator"> + </constant> + <constant name="OP_DIV" value="3" enum="Operator"> + </constant> + <constant name="OP_MOD" value="4" enum="Operator"> + </constant> + <constant name="OP_POW" value="5" enum="Operator"> + </constant> + <constant name="OP_MAX" value="6" enum="Operator"> + </constant> + <constant name="OP_MIN" value="7" enum="Operator"> + </constant> + <constant name="OP_CROSS" value="8" enum="Operator"> + </constant> + </constants> +</class> diff --git a/drivers/SCsub b/drivers/SCsub index 2c5e9434e8..f9cfa3fb05 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -21,6 +21,11 @@ if (env["platform"] == "windows"): if env['xaudio2']: SConscript("xaudio2/SCsub") +# Midi drivers +SConscript('alsamidi/SCsub') +SConscript('coremidi/SCsub') +SConscript('winmidi/SCsub') + # Graphics drivers if (env["platform"] != "server"): SConscript('gles3/SCsub') diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index ae85ee50d1..a44a11a46d 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -58,7 +58,10 @@ Error AudioDriverALSA::init_device() { #define CHECK_FAIL(m_cond) \ if (m_cond) { \ fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \ - snd_pcm_close(pcm_handle); \ + if (pcm_handle) { \ + snd_pcm_close(pcm_handle); \ + pcm_handle = NULL; \ + } \ ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN); \ } @@ -150,7 +153,6 @@ Error AudioDriverALSA::init() { active = false; thread_exited = false; exit_thread = false; - pcm_open = false; Error err = init_device(); if (err == OK) { @@ -172,14 +174,14 @@ void AudioDriverALSA::thread_func(void *p_udata) { if (!ad->active) { for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) { - ad->samples_out[i] = 0; + ad->samples_out.write[i] = 0; } } else { ad->audio_server_process(ad->period_size, ad->samples_in.ptrw()); for (unsigned int i = 0; i < ad->period_size * ad->channels; i++) { - ad->samples_out[i] = ad->samples_in[i] >> 16; + ad->samples_out.write[i] = ad->samples_in[i] >> 16; } } @@ -313,9 +315,9 @@ void AudioDriverALSA::unlock() { void AudioDriverALSA::finish_device() { - if (pcm_open) { + if (pcm_handle) { snd_pcm_close(pcm_handle); - pcm_open = NULL; + pcm_handle = NULL; } } diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index 2878e100a2..e2a2325cf3 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -66,7 +66,6 @@ class AudioDriverALSA : public AudioDriver { bool active; bool thread_exited; mutable bool exit_thread; - bool pcm_open; public: const char *get_name() const { diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/alsamidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/alsamidi/alsa_midi.cpp b/drivers/alsamidi/alsa_midi.cpp new file mode 100644 index 0000000000..599470d7e0 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* alsa_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#include <errno.h> + +#include "alsa_midi.h" +#include "os/os.h" +#include "print_string.h" + +static int get_message_size(uint8_t message) { + switch (message & 0xF0) { + case 0x80: // note off + case 0x90: // note on + case 0xA0: // aftertouch + case 0xB0: // continuous controller + return 3; + + case 0xC0: // patch change + case 0xD0: // channel pressure + case 0xE0: // pitch bend + return 2; + } + + return 256; +} + +void MIDIDriverALSAMidi::thread_func(void *p_udata) { + MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata; + uint64_t timestamp = 0; + uint8_t buffer[256]; + int expected_size = 255; + int bytes = 0; + + while (!md->exit_thread) { + int ret; + + md->lock(); + + for (int i = 0; i < md->connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = md->connected_inputs[i]; + do { + uint8_t byte = 0; + ret = snd_rawmidi_read(midi_in, &byte, 1); + if (ret < 0) { + if (ret != -EAGAIN) { + ERR_PRINTS("snd_rawmidi_read error: " + String(snd_strerror(ret))); + } + } else { + if (byte & 0x80) { + // Flush previous packet if there is any + if (bytes) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + expected_size = get_message_size(byte); + } + + if (bytes < 256) { + buffer[bytes++] = byte; + // If we know the size of the current packet receive it if it reached the expected size + if (bytes >= expected_size) { + md->receive_input_packet(timestamp, buffer, bytes); + bytes = 0; + } + } + } + } while (ret > 0); + } + + md->unlock(); + + OS::get_singleton()->delay_usec(1000); + } +} + +Error MIDIDriverALSAMidi::open() { + + void **hints; + + if (snd_device_name_hint(-1, "rawmidi", &hints) < 0) + return ERR_CANT_OPEN; + + int i = 0; + for (void **n = hints; *n != NULL; n++) { + char *name = snd_device_name_get_hint(*n, "NAME"); + + if (name != NULL) { + snd_rawmidi_t *midi_in; + int ret = snd_rawmidi_open(&midi_in, NULL, name, SND_RAWMIDI_NONBLOCK); + if (ret >= 0) { + connected_inputs.insert(i++, midi_in); + } + } + + if (name != NULL) + free(name); + } + snd_device_name_free_hint(hints); + + mutex = Mutex::create(); + thread = Thread::create(MIDIDriverALSAMidi::thread_func, this); + + return OK; +} + +void MIDIDriverALSAMidi::close() { + + if (thread) { + exit_thread = true; + Thread::wait_to_finish(thread); + + memdelete(thread); + thread = NULL; + } + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } + + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_close(midi_in); + } + connected_inputs.clear(); +} + +void MIDIDriverALSAMidi::lock() const { + + if (mutex) + mutex->lock(); +} + +void MIDIDriverALSAMidi::unlock() const { + + if (mutex) + mutex->unlock(); +} + +PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() { + + PoolStringArray list; + + lock(); + for (int i = 0; i < connected_inputs.size(); i++) { + snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_info_t *info; + + snd_rawmidi_info_malloc(&info); + snd_rawmidi_info(midi_in, info); + list.push_back(snd_rawmidi_info_get_name(info)); + snd_rawmidi_info_free(info); + } + unlock(); + + return list; +} + +MIDIDriverALSAMidi::MIDIDriverALSAMidi() { + + mutex = NULL; + thread = NULL; + + exit_thread = false; +} + +MIDIDriverALSAMidi::~MIDIDriverALSAMidi() { + + close(); +} + +#endif diff --git a/drivers/alsamidi/alsa_midi.h b/drivers/alsamidi/alsa_midi.h new file mode 100644 index 0000000000..90e458a365 --- /dev/null +++ b/drivers/alsamidi/alsa_midi.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/* alsa_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef ALSAMIDI_ENABLED + +#ifndef ALSA_MIDI_H +#define ALSA_MIDI_H + +#include <alsa/asoundlib.h> +#include <stdio.h> + +#include "core/os/mutex.h" +#include "core/os/thread.h" +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverALSAMidi : public MIDIDriver { + + Thread *thread; + Mutex *mutex; + + Vector<snd_rawmidi_t *> connected_inputs; + + bool exit_thread; + + static void thread_func(void *p_udata); + + void lock() const; + void unlock() const; + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverALSAMidi(); + virtual ~MIDIDriverALSAMidi(); +}; + +#endif +#endif diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index ac21de91e4..e1f47cb8c2 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -35,8 +35,23 @@ #include "os/os.h" #define kOutputBus 0 +#define kInputBus 1 #ifdef OSX_ENABLED +OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData) { + AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + + // If our selected device is the Default call set_device to update the + // kAudioOutputUnitProperty_CurrentDevice property + if (driver->capture_device_name == "Default") { + driver->capture_set_device("Default"); + } + + return noErr; +} + OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { @@ -79,6 +94,11 @@ Error AudioDriverCoreAudio::init() { result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); ERR_FAIL_COND_V(result != noErr, FAILED); + + prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; + + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); + ERR_FAIL_COND_V(result != noErr, FAILED); #endif AudioStreamBasicDescription strdesc; @@ -102,6 +122,26 @@ Error AudioDriverCoreAudio::init() { break; } + zeromem(&strdesc, sizeof(strdesc)); + size = sizeof(strdesc); + result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size); + ERR_FAIL_COND_V(result != noErr, FAILED); + + switch (strdesc.mChannelsPerFrame) { + case 1: // Mono + capture_channels = 1; + break; + + case 2: // Stereo + capture_channels = 2; + break; + + default: + // Unknown number of channels, default to stereo + capture_channels = 2; + break; + } + mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); zeromem(&strdesc, sizeof(strdesc)); @@ -117,6 +157,11 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); + strdesc.mChannelsPerFrame = capture_channels; + + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc)); + ERR_FAIL_COND_V(result != noErr, FAILED); + int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_frames = closest_power_of_2(latency * mix_rate / 1000); @@ -126,8 +171,12 @@ Error AudioDriverCoreAudio::init() { ERR_FAIL_COND_V(result != noErr, FAILED); #endif - buffer_size = buffer_frames * channels; + unsigned int buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); + input_buf.resize(buffer_size); + input_buffer.resize(buffer_size * 8); + input_position = 0; + input_size = 0; if (OS::get_singleton()->is_stdout_verbose()) { print_line("CoreAudio: detected " + itos(channels) + " channels"); @@ -141,6 +190,12 @@ Error AudioDriverCoreAudio::init() { result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); ERR_FAIL_COND_V(result != noErr, FAILED); + zeromem(&callback, sizeof(AURenderCallbackStruct)); + callback.inputProc = &AudioDriverCoreAudio::input_callback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); + ERR_FAIL_COND_V(result != noErr, FAILED); + result = AudioUnitInitialize(audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); @@ -192,6 +247,45 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, return 0; }; +OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) { + + AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + if (!ad->active) { + return 0; + } + + ad->lock(); + + AudioBufferList bufferList; + bufferList.mNumberBuffers = 1; + bufferList.mBuffers[0].mData = ad->input_buf.ptrw(); + bufferList.mBuffers[0].mNumberChannels = ad->capture_channels; + bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t); + + OSStatus result = AudioUnitRender(ad->audio_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); + if (result == noErr) { + for (int i = 0; i < inNumberFrames * ad->capture_channels; i++) { + int32_t sample = ad->input_buf[i] << 16; + ad->input_buffer_write(sample); + + if (ad->capture_channels == 1) { + // In case input device is single channel convert it to Stereo + ad->input_buffer_write(sample); + } + } + } else { + ERR_PRINT(("AudioUnitRender failed, code: " + itos(result)).utf8().get_data()); + } + + ad->unlock(); + + return result; +} + void AudioDriverCoreAudio::start() { if (!active) { OSStatus result = AudioOutputUnitStart(audio_unit); @@ -222,9 +316,94 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); }; +void AudioDriverCoreAudio::lock() { + if (mutex) + mutex->lock(); +}; + +void AudioDriverCoreAudio::unlock() { + if (mutex) + mutex->unlock(); +}; + +bool AudioDriverCoreAudio::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + +void AudioDriverCoreAudio::finish() { + OSStatus result; + + lock(); + + AURenderCallbackStruct callback; + zeromem(&callback, sizeof(AURenderCallbackStruct)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); + if (result != noErr) { + ERR_PRINT("AudioUnitSetProperty failed"); + } + + if (active) { + result = AudioOutputUnitStop(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioOutputUnitStop failed"); + } + + active = false; + } + + result = AudioUnitUninitialize(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioUnitUninitialize failed"); + } + #ifdef OSX_ENABLED + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; -Array AudioDriverCoreAudio::get_device_list() { + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } +#endif + + result = AudioComponentInstanceDispose(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioComponentInstanceDispose failed"); + } + + unlock(); + + if (mutex) { + memdelete(mutex); + mutex = NULL; + } +}; + +Error AudioDriverCoreAudio::capture_start() { + + UInt32 flag = 1; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +Error AudioDriverCoreAudio::capture_stop() { + + UInt32 flag = 0; + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +#ifdef OSX_ENABLED + +Array AudioDriverCoreAudio::_get_device_list(bool capture) { Array list; @@ -243,20 +422,20 @@ Array AudioDriverCoreAudio::get_device_list() { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -281,21 +460,11 @@ Array AudioDriverCoreAudio::get_device_list() { return list; } -String AudioDriverCoreAudio::get_device() { - - return device_name; -} - -void AudioDriverCoreAudio::set_device(String device) { - - device_name = device; - if (!active) { - return; - } +void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { AudioDeviceID deviceId; bool found = false; - if (device_name != "Default") { + if (device != "Default") { AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; @@ -309,20 +478,20 @@ void AudioDriverCoreAudio::set_device(String device) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount && !found; i++) { - prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); AudioBufferList *bufferList = (AudioBufferList *)malloc(size); AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); - UInt32 outputChannelCount = 0; + UInt32 channelCount = 0; for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) - outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + channelCount += bufferList->mBuffers[j].mNumberChannels; free(bufferList); - if (outputChannelCount >= 1) { + if (channelCount >= 1) { CFStringRef cfname; size = sizeof(CFStringRef); @@ -335,7 +504,7 @@ void AudioDriverCoreAudio::set_device(String device) { char *buffer = (char *)malloc(maxSize); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; - if (name == device_name) { + if (name == device) { deviceId = audioDevices[i]; found = true; } @@ -351,7 +520,8 @@ void AudioDriverCoreAudio::set_device(String device) { if (!found) { // If we haven't found the desired device get the system default one UInt32 size = sizeof(AudioDeviceID); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); ERR_FAIL_COND(result != noErr); @@ -360,79 +530,52 @@ void AudioDriverCoreAudio::set_device(String device) { } if (found) { - OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); + + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; } } -#endif - -void AudioDriverCoreAudio::lock() { - if (mutex) - mutex->lock(); -}; - -void AudioDriverCoreAudio::unlock() { - if (mutex) - mutex->unlock(); -}; +Array AudioDriverCoreAudio::get_device_list() { -bool AudioDriverCoreAudio::try_lock() { - if (mutex) - return mutex->try_lock() == OK; - return true; + return _get_device_list(); } -void AudioDriverCoreAudio::finish() { - OSStatus result; +String AudioDriverCoreAudio::get_device() { - lock(); + return device_name; +} - AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); - if (result != noErr) { - ERR_PRINT("AudioUnitSetProperty failed"); - } +void AudioDriverCoreAudio::set_device(String device) { + device_name = device; if (active) { - result = AudioOutputUnitStop(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioOutputUnitStop failed"); - } - - active = false; + _set_device(device_name); } +} - result = AudioUnitUninitialize(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioUnitUninitialize failed"); +void AudioDriverCoreAudio::capture_set_device(const String &p_name) { + + capture_device_name = p_name; + if (active) { + _set_device(capture_device_name, true); } +} -#ifdef OSX_ENABLED - AudioObjectPropertyAddress prop; - prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - prop.mScope = kAudioObjectPropertyScopeGlobal; - prop.mElement = kAudioObjectPropertyElementMaster; +Array AudioDriverCoreAudio::capture_get_device_list() { - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); - if (result != noErr) { - ERR_PRINT("AudioObjectRemovePropertyListener failed"); - } -#endif + return _get_device_list(true); +} - result = AudioComponentInstanceDispose(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioComponentInstanceDispose failed"); - } +String AudioDriverCoreAudio::capture_get_device() { - unlock(); + return capture_device_name; +} - if (mutex) { - memdelete(mutex); - mutex = NULL; - } -}; +#endif AudioDriverCoreAudio::AudioDriverCoreAudio() { active = false; @@ -440,14 +583,15 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() { mix_rate = 0; channels = 2; + capture_channels = 2; - buffer_size = 0; buffer_frames = 0; samples_in.clear(); device_name = "Default"; -}; + capture_device_name = "Default"; +} AudioDriverCoreAudio::~AudioDriverCoreAudio(){}; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 99c910498e..d3f7c8d596 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -48,15 +48,24 @@ class AudioDriverCoreAudio : public AudioDriver { Mutex *mutex; String device_name; + String capture_device_name; int mix_rate; unsigned int channels; + unsigned int capture_channels; unsigned int buffer_frames; - unsigned int buffer_size; Vector<int32_t> samples_in; + Vector<int16_t> input_buf; #ifdef OSX_ENABLED + Array _get_device_list(bool capture = false); + void _set_device(const String &device, bool capture = false); + + static OSStatus input_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData); + static OSStatus output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData); @@ -68,6 +77,12 @@ class AudioDriverCoreAudio : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + static OSStatus input_callback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + public: const char *get_name() const { return "CoreAudio"; @@ -77,18 +92,27 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; -#ifdef OSX_ENABLED - virtual Array get_device_list(); - virtual String get_device(); - virtual void set_device(String device); -#endif + virtual void lock(); virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + bool try_lock(); void stop(); +#ifdef OSX_ENABLED + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); + + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); +#endif + AudioDriverCoreAudio(); ~AudioDriverCoreAudio(); }; diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/coremidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/coremidi/core_midi.cpp b/drivers/coremidi/core_midi.cpp new file mode 100644 index 0000000000..3619be4a8e --- /dev/null +++ b/drivers/coremidi/core_midi.cpp @@ -0,0 +1,105 @@ +/*************************************************************************/ +/* core_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#include "core_midi.h" +#include "print_string.h" + +#include <CoreAudio/HostTime.h> +#include <CoreServices/CoreServices.h> + +void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) { + MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet); + for (int i = 0; i < packet_list->numPackets; i++) { + receive_input_packet(packet->timeStamp, packet->data, packet->length); + packet = MIDIPacketNext(packet); + } +} + +Error MIDIDriverCoreMidi::open() { + + CFStringRef name = CFStringCreateWithCString(NULL, "Godot", kCFStringEncodingASCII); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client); + CFRelease(name); + if (result != noErr) { + ERR_PRINTS("MIDIClientCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + result = MIDIInputPortCreate(client, CFSTR("Godot Input"), MIDIDriverCoreMidi::read, (void *)this, &port_in); + if (result != noErr) { + ERR_PRINTS("MIDIInputPortCreate failed: " + String(GetMacOSStatusErrorString(result))); + return ERR_CANT_OPEN; + } + + int sources = MIDIGetNumberOfSources(); + for (int i = 0; i < sources; i++) { + + MIDIEndpointRef source = MIDIGetSource(i); + if (source != NULL) { + MIDIPortConnectSource(port_in, source, (void *)this); + connected_sources.insert(i, source); + } + } + + return OK; +} + +void MIDIDriverCoreMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + MIDIEndpointRef source = connected_sources[i]; + MIDIPortDisconnectSource(port_in, source); + } + connected_sources.clear(); + + if (port_in != 0) { + MIDIPortDispose(port_in); + port_in = 0; + } + + if (client != 0) { + MIDIClientDispose(client); + client = 0; + } +} + +MIDIDriverCoreMidi::MIDIDriverCoreMidi() { + + client = 0; +} + +MIDIDriverCoreMidi::~MIDIDriverCoreMidi() { + + close(); +} + +#endif diff --git a/drivers/coremidi/core_midi.h b/drivers/coremidi/core_midi.h new file mode 100644 index 0000000000..fd35e12f4b --- /dev/null +++ b/drivers/coremidi/core_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* core_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef COREMIDI_ENABLED + +#ifndef CORE_MIDI_H +#define CORE_MIDI_H + +#include <stdio.h> + +#include <CoreMIDI/CoreMIDI.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverCoreMidi : public MIDIDriver { + + MIDIClientRef client; + MIDIPortRef port_in; + + Vector<MIDIEndpointRef> connected_sources; + + static void read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con); + +public: + virtual Error open(); + virtual void close(); + + MIDIDriverCoreMidi(); + virtual ~MIDIDriverCoreMidi(); +}; + +#endif +#endif diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index bab89f649a..e39ec915fc 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -154,7 +154,8 @@ public: ERR_FAIL_COND_V(!texture, RID()); return texture_owner.make_rid(texture); } - void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) { + + void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type = VS::TEXTURE_TYPE_2D, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND(!t); t->width = p_width; @@ -164,7 +165,7 @@ public: t->image = Ref<Image>(memnew(Image)); t->image->create(p_width, p_height, false, p_format); } - void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) { + void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_level) { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND(!t); t->width = p_image->get_width(); @@ -173,7 +174,7 @@ public: t->image->create(t->width, t->height, false, t->format, p_image->get_data()); } - void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { + void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_level) { DummyTexture *t = texture_owner.get(p_texture); ERR_FAIL_COND(!t); @@ -186,7 +187,7 @@ public: t->image->blit_rect(p_image, Rect2(src_x, src_y, src_w, src_h), Vector2(dst_x, dst_y)); } - Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const { + Ref<Image> texture_get_data(RID p_texture, int p_level) const { DummyTexture *t = texture_owner.getornull(p_texture); ERR_FAIL_COND_V(!t, Ref<Image>()); return t->image; @@ -206,10 +207,13 @@ public: ERR_FAIL_COND_V(!t, Image::FORMAT_RGB8); return t->format; } + + VisualServer::TextureType texture_get_type(RID p_texture) const { return VS::TEXTURE_TYPE_2D; } uint32_t texture_get_texid(RID p_texture) const { return 0; } uint32_t texture_get_width(RID p_texture) const { return 0; } uint32_t texture_get_height(RID p_texture) const { return 0; } - void texture_set_size_override(RID p_texture, int p_width, int p_height) {} + uint32_t texture_get_depth(RID p_texture) const { return 0; } + void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) {} void texture_set_path(RID p_texture, const String &p_path) { DummyTexture *t = texture_owner.getornull(p_texture); @@ -289,7 +293,7 @@ public: ERR_FAIL_COND(!m); m->surfaces.push_back(DummySurface()); - DummySurface *s = &m->surfaces[m->surfaces.size() - 1]; + DummySurface *s = &m->surfaces.write[m->surfaces.size() - 1]; s->format = p_format; s->primitive = p_primitive; s->array = p_array; @@ -777,7 +781,7 @@ public: void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale) {} void initialize() {} - void begin_frame() {} + void begin_frame(double frame_step) {} void set_current_render_target(RID p_render_target) {} void restore_render_target() {} void clear_render_target(const Color &p_color) {} diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp index 6d3e176bbb..b099019d17 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/dummy/texture_loader_dummy.cpp @@ -45,10 +45,6 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi dstbuff.resize(rowsize * height); - PoolVector<uint8_t>::Write dstbuff_write = dstbuff.write(); - - uint8_t *data = dstbuff_write.ptr(); - uint8_t **row_p = memnew_arr(uint8_t *, height); for (unsigned int i = 0; i < height; i++) { diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index ea52153285..3d388c031a 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -54,10 +54,26 @@ void RasterizerCanvasGLES2::_set_uniforms() { state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX, state.uniforms.extra_matrix); state.canvas_shader.set_uniform(CanvasShaderGLES2::FINAL_MODULATE, state.uniforms.final_modulate); + + state.canvas_shader.set_uniform(CanvasShaderGLES2::TIME, storage->frame.time[0]); + + if (storage->frame.current_rt) { + Vector2 screen_pixel_size; + screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + + state.canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + } } void RasterizerCanvasGLES2::canvas_begin() { + state.canvas_shader.bind(); + if (storage->frame.current_rt) { + glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); + glColorMask(1, 1, 1, 1); + } + if (storage->frame.clear_request) { glClearColor(storage->frame.clear_request_color.r, storage->frame.clear_request_color.g, @@ -67,10 +83,12 @@ void RasterizerCanvasGLES2::canvas_begin() { storage->frame.clear_request = false; } + /* if (storage->frame.current_rt) { glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); glColorMask(1, 1, 1, 1); } + */ reset_canvas(); @@ -312,7 +330,7 @@ void RasterizerCanvasGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_v glBindBuffer(GL_ARRAY_BUFFER, 0); } -void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) { +void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material) { int command_count = p_item->commands.size(); Item::Command **commands = p_item->commands.ptrw(); @@ -329,9 +347,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - state.canvas_shader.bind(); - - _set_uniforms(); + if (state.canvas_shader.bind()) { + _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } _bind_canvas_texture(RID(), RID()); @@ -359,7 +378,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur _draw_gui_primitive(4, verts, NULL, NULL); } - } break; case Item::Command::TYPE_RECT: { @@ -373,8 +391,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map); @@ -454,8 +474,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components); @@ -472,8 +494,16 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur // state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + Rect2 source = np->source; + if (source.size.x == 0 && source.size.y == 0) { + source.size.x = tex->width; + source.size.y = tex->height; + } + // prepare vertex buffer + // this buffer contains [ POS POS UV UV ] * + float buffer[16 * 2 + 16 * 2]; { @@ -483,106 +513,106 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur buffer[(0 * 4 * 4) + 0] = np->rect.position.x; buffer[(0 * 4 * 4) + 1] = np->rect.position.y; - buffer[(0 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(0 * 4 * 4) + 3] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(0 * 4 * 4) + 5] = np->rect.position.y; - buffer[(0 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 7] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(0 * 4 * 4) + 9] = np->rect.position.y; - buffer[(0 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 11] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 11] = source.position.y * texpixel_size.y; buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(0 * 4 * 4) + 13] = np->rect.position.y; - buffer[(0 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(0 * 4 * 4) + 15] = np->source.position.y * texpixel_size.y; + buffer[(0 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(0 * 4 * 4) + 15] = source.position.y * texpixel_size.y; // second row buffer[(1 * 4 * 4) + 0] = np->rect.position.x; buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(1 * 4 * 4) + 3] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 7] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 11] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP]; - buffer[(1 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(1 * 4 * 4) + 15] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; // thrid row buffer[(2 * 4 * 4) + 0] = np->rect.position.x; buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(2 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM]; - buffer[(2 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(2 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; // fourth row buffer[(3 * 4 * 4) + 0] = np->rect.position.x; buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(3 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x; + buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT]; buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 11] = (source.position.y + source.size.y) * texpixel_size.y; buffer[(3 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; buffer[(3 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y; - buffer[(3 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(3 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + buffer[(3 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x; + buffer[(3 * 4 * 4) + 15] = (source.position.y + source.size.y) * texpixel_size.y; - // print_line(String::num((np->source.position.y + np->source.size.y) * texpixel_size.y)); + // print_line(String::num((source.position.y + source.size.y) * texpixel_size.y)); } glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); @@ -610,8 +640,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } static const int num_points = 32; @@ -639,8 +671,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map); @@ -658,8 +692,12 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } + + _bind_canvas_texture(RID(), RID()); if (pline->triangles.size()) { _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1); @@ -687,8 +725,10 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (state.canvas_shader.bind()) + if (state.canvas_shader.bind()) { _set_uniforms(); + state.canvas_shader.use_material((void *)p_material); + } ERR_CONTINUE(primitive->points.size() < 1); @@ -757,42 +797,6 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur void RasterizerCanvasGLES2::_copy_texscreen(const Rect2 &p_rect) { // This isn't really working yet, so disabling for now. - - /* - glDisable(GL_BLEND); - - state.canvas_texscreen_used = true; - - Vector2 wh(storage->frame.current_rt->width, storage->frame.current_rt->height); - Color copy_section(p_rect.position.x / wh.x, p_rect.position.y / wh.y, p_rect.size.x / wh.x, p_rect.size.y / wh.y); - - if (p_rect != Rect2()) { - // only use section - - storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, true); - } - - - storage->shaders.copy.bind(); - storage->shaders.copy.set_uniform(CopyShaderGLES2::COPY_SECTION, copy_section); - - _bind_quad_buffer(); - - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->copy_screen_effect.fbo); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisableVertexAttribArray(VS::ARRAY_VERTEX); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - - state.canvas_shader.bind(); - _bind_canvas_texture(state.current_tex, state.current_normal); - - glEnable(GL_BLEND); - */ } void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { @@ -848,10 +852,10 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons Item *material_owner = ci->material_owner ? ci->material_owner : ci; RID material = material_owner->material; + RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); if (material != canvas_last_material || rebind_shader) { - RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); RasterizerStorageGLES2::Shader *shader_ptr = NULL; if (material_ptr) { @@ -878,7 +882,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons } int tc = material_ptr->textures.size(); - RID *textures = material_ptr->textures.ptrw(); + Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw(); @@ -886,7 +890,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons glActiveTexture(GL_TEXTURE2 + i); - RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i]); + RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second); if (!t) { @@ -917,10 +921,12 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons glBindTexture(t->target, t->tex_id); } + } else { state.canvas_shader.set_custom_shader(0); state.canvas_shader.bind(); } + state.canvas_shader.use_material((void *)material_ptr); shader_cache = shader_ptr; @@ -975,7 +981,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons _set_uniforms(); - _canvas_item_render_commands(p_item_list, NULL, reclip); + _canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr); rebind_shader = true; // hacked in for now. diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index 4eab8c6038..cda3ec79e7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -103,7 +103,7 @@ public: _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); _FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); - _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip); + _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material); _FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect); virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 9ea20ff15a..a1a0b9e2c6 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -227,21 +227,14 @@ void RasterizerGLES2::initialize() { scene->initialize(); } -void RasterizerGLES2::begin_frame() { - uint64_t tick = OS::get_singleton()->get_ticks_usec(); +void RasterizerGLES2::begin_frame(double frame_step) { + time_total += frame_step; - double delta = double(tick - prev_ticks) / 1000000.0; - delta *= Engine::get_singleton()->get_time_scale(); - - time_total += delta; - - if (delta == 0) { + if (frame_step == 0) { //to avoid hiccups - delta = 0.001; + frame_step = 0.001; } - prev_ticks = tick; - // double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); // if (time_total > time_roll_over) // time_total = 0; //roll over every day (should be customz @@ -251,9 +244,7 @@ void RasterizerGLES2::begin_frame() { storage->frame.time[2] = Math::fmod(time_total, 900); storage->frame.time[3] = Math::fmod(time_total, 60); storage->frame.count++; - storage->frame.delta = delta; - - storage->frame.prev_tick = tick; + storage->frame.delta = frame_step; storage->update_dirty_resources(); @@ -326,7 +317,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c canvas->canvas_begin(); RID texture = storage->texture_create(); - storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), VS::TEXTURE_FLAG_FILTER); + storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); storage->texture_set_data(texture, p_image); Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); @@ -344,28 +335,7 @@ void RasterizerGLES2::set_boot_image(const Ref<Image> &p_image, const Color &p_c storage->free(texture); - if (OS::get_singleton()->is_layered_allowed()) { - if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) - Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); - uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); - if (data) { - glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data); - OS::get_singleton()->swap_layered_buffer(); - - return; - } -#endif - } else { - //clear alpha - glColorMask(false, false, false, true); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glColorMask(true, true, true, true); - } - } - - OS::get_singleton()->swap_buffers(); + end_frame(true); } void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) { @@ -378,10 +348,10 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); + canvas->state.canvas_shader.set_custom_shader(0); canvas->state.canvas_shader.bind(); canvas->canvas_begin(); - canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, true); glDisable(GL_BLEND); glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); glActiveTexture(GL_TEXTURE0); @@ -391,8 +361,6 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1)); - canvas->state.canvas_shader.set_uniform(CanvasShaderGLES2::BLIT_PASS, false); - glBindTexture(GL_TEXTURE_2D, 0); canvas->canvas_end(); } @@ -452,7 +420,6 @@ RasterizerGLES2::RasterizerGLES2() { scene->storage = storage; storage->scene = scene; - prev_ticks = 0; time_total = 0; } diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index 8d57275449..f727af39dd 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -43,7 +43,6 @@ class RasterizerGLES2 : public Rasterizer { RasterizerCanvasGLES2 *canvas; RasterizerSceneGLES2 *scene; - uint64_t prev_ticks; double time_total; public: @@ -54,7 +53,7 @@ public: virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale); virtual void initialize(); - virtual void begin_frame(); + virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index f7712be5d0..5f31bfe209 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -28,38 +28,398 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "rasterizer_scene_gles2.h" +#include "math/transform.h" #include "math_funcs.h" #include "os/os.h" #include "project_settings.h" #include "rasterizer_canvas_gles2.h" #include "servers/visual/visual_server_raster.h" +#include "vmap.h" + #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif +static const GLenum _cube_side_enum[6] = { + + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + +}; + /* SHADOW ATLAS API */ RID RasterizerSceneGLES2::shadow_atlas_create() { - return RID(); + ShadowAtlas *shadow_atlas = memnew(ShadowAtlas); + shadow_atlas->fbo = 0; + shadow_atlas->depth = 0; + shadow_atlas->size = 0; + shadow_atlas->smallest_subdiv = 0; + + for (int i = 0; i < 4; i++) { + shadow_atlas->size_order[i] = i; + } + + return shadow_atlas_owner.make_rid(shadow_atlas); } void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(p_size < 0); + + p_size = next_power_of_2(p_size); + + if (p_size == shadow_atlas->size) + return; + + // erase the old atlast + if (shadow_atlas->fbo) { + glDeleteTextures(1, &shadow_atlas->depth); + glDeleteFramebuffers(1, &shadow_atlas->fbo); + + shadow_atlas->fbo = 0; + shadow_atlas->depth = 0; + } + + // erase shadow atlast references from lights + for (Map<RID, uint32_t>::Element *E = shadow_atlas->shadow_owners.front(); E; E = E->next()) { + LightInstance *li = light_instance_owner.getornull(E->key()); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + + shadow_atlas->shadow_owners.clear(); + + shadow_atlas->size = p_size; + + if (shadow_atlas->size) { + glGenFramebuffers(1, &shadow_atlas->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo); + + // create a depth texture + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &shadow_atlas->depth); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, shadow_atlas->size, shadow_atlas->size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_atlas->depth, 0); + + glViewport(0, 0, shadow_atlas->size, shadow_atlas->size); + + glDepthMask(GL_TRUE); + + glClearDepth(0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } } void RasterizerSceneGLES2::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_INDEX(p_quadrant, 4); + ERR_FAIL_INDEX(p_subdivision, 16384); + + uint32_t subdiv = next_power_of_2(p_subdivision); + if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer + subdiv <<= 1; + } + + subdiv = int(Math::sqrt((float)subdiv)); + + if (shadow_atlas->quadrants[p_quadrant].shadows.size() == subdiv) + return; + + // erase all data from the quadrant + for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) { + if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) { + shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + + LightInstance *li = light_instance_owner.getornull(shadow_atlas->quadrants[p_quadrant].shadows[i].owner); + ERR_CONTINUE(!li); + li->shadow_atlases.erase(p_atlas); + } + } + + shadow_atlas->quadrants[p_quadrant].shadows.resize(0); + shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv); + shadow_atlas->quadrants[p_quadrant].subdivision = subdiv; + + // cache the smallest subdivision for faster allocations + + shadow_atlas->smallest_subdiv = 1 << 30; + + for (int i = 0; i < 4; i++) { + if (shadow_atlas->quadrants[i].subdivision) { + shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision); + } + } + + if (shadow_atlas->smallest_subdiv == 1 << 30) { + shadow_atlas->smallest_subdiv = 0; + } + + // re-sort the quadrants + + int swaps = 0; + do { + swaps = 0; + + for (int i = 0; i < 3; i++) { + if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) { + SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]); + swaps++; + } + } + + } while (swaps > 0); +} + +bool RasterizerSceneGLES2::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow) { + + for (int i = p_quadrant_count - 1; i >= 0; i--) { + int qidx = p_in_quadrants[i]; + + if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) { + return false; + } + + // look for an empty space + + int sc = shadow_atlas->quadrants[qidx].shadows.size(); + + ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptrw(); + + int found_free_idx = -1; // found a free one + int found_used_idx = -1; // found an existing one, must steal it + uint64_t min_pass = 0; // pass of the existing one, try to use the least recently + + for (int j = 0; j < sc; j++) { + if (!sarr[j].owner.is_valid()) { + found_free_idx = j; + break; + } + + LightInstance *sli = light_instance_owner.getornull(sarr[j].owner); + ERR_CONTINUE(!sli); + + if (sli->last_scene_pass != scene_pass) { + + // was just allocated, don't kill it so soon, wait a bit... + + if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) { + continue; + } + + if (found_used_idx == -1 || sli->last_scene_pass < min_pass) { + found_used_idx = j; + min_pass = sli->last_scene_pass; + } + } + } + + if (found_free_idx == -1 && found_used_idx == -1) { + continue; // nothing found + } + + if (found_free_idx == -1 && found_used_idx != -1) { + found_free_idx = found_used_idx; + } + + r_quadrant = qidx; + r_shadow = found_free_idx; + + return true; + } + + return false; } bool RasterizerSceneGLES2::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas); + ERR_FAIL_COND_V(!shadow_atlas, false); + + LightInstance *li = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!li, false); + + if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) { + return false; + } + + uint32_t quad_size = shadow_atlas->size >> 1; + int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage)); + + int valid_quadrants[4]; + int valid_quadrant_count = 0; + int best_size = -1; + int best_subdiv = -1; + + for (int i = 0; i < 4; i++) { + int q = shadow_atlas->size_order[i]; + int sd = shadow_atlas->quadrants[q].subdivision; + + if (sd == 0) { + continue; + } + + int max_fit = quad_size / sd; + + if (best_size != -1 && max_fit > best_size) { + break; // what we asked for is bigger than this. + } + + valid_quadrants[valid_quadrant_count] = q; + valid_quadrant_count++; + + best_subdiv = sd; + + if (max_fit >= desired_fit) { + best_size = max_fit; + } + } + + ERR_FAIL_COND_V(valid_quadrant_count == 0, false); // no suitable block available + + uint64_t tick = OS::get_singleton()->get_ticks_msec(); + + if (shadow_atlas->shadow_owners.has(p_light_intance)) { + // light was already known! + + uint32_t key = shadow_atlas->shadow_owners[p_light_intance]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + bool should_realloc = shadow_atlas->quadrants[q].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[q].shadows[s].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec); + + bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version; + + if (!should_realloc) { + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + return should_redraw; + } + + int new_quadrant; + int new_shadow; + + // find a better place + + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) { + // found a better place + + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + // it is take but invalid, so we can take it + + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.get(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + // erase previous + shadow_atlas->quadrants[q].shadows.write[s].version = 0; + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + // make a new key + key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + + // update it in the map + shadow_atlas->shadow_owners[p_light_intance] = key; + + // make it dirty, so we redraw + return true; + } + + // no better place found, so we keep the current place + + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; + + return should_redraw; + } + + int new_quadrant; + int new_shadow; + + if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) { + // found a better place + + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; + if (sh->owner.is_valid()) { + // it is take but invalid, so we can take it + + shadow_atlas->shadow_owners.erase(sh->owner); + LightInstance *sli = light_instance_owner.get(sh->owner); + sli->shadow_atlases.erase(p_atlas); + } + + sh->owner = p_light_intance; + sh->alloc_tick = tick; + sh->version = p_light_version; + li->shadow_atlases.insert(p_atlas); + + // make a new key + uint32_t key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT; + key |= new_shadow; + + // update it in the map + shadow_atlas->shadow_owners[p_light_intance] = key; + + // make it dirty, so we redraw + return true; + } + return false; } void RasterizerSceneGLES2::set_directional_shadow_count(int p_count) { + directional_shadow.light_count = p_count; + directional_shadow.current_light = 0; } int RasterizerSceneGLES2::get_directional_light_shadow_size(RID p_light_intance) { - return 0; + + ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0); + + int shadow_size; + + if (directional_shadow.light_count == 1) { + shadow_size = directional_shadow.size; + } else { + shadow_size = directional_shadow.size / 2; //more than 4 not supported anyway + } + + LightInstance *light_instance = light_instance_owner.getornull(p_light_intance); + ERR_FAIL_COND_V(!light_instance, 0); + + switch (light_instance->light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: + break; //none + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: + shadow_size /= 2; + break; + } + + return shadow_size; } ////////////////////////////////////////////////////// @@ -105,86 +465,183 @@ bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_inst RID RasterizerSceneGLES2::environment_create() { - return RID(); + Environment *env = memnew(Environment); + + return environment_owner.make_rid(env); } void RasterizerSceneGLES2::environment_set_background(RID p_env, VS::EnvironmentBG p_bg) { + + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + env->bg_mode = p_bg; } void RasterizerSceneGLES2::environment_set_sky(RID p_env, RID p_sky) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->sky = p_sky; } void RasterizerSceneGLES2::environment_set_sky_custom_fov(RID p_env, float p_scale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->sky_custom_fov = p_scale; } void RasterizerSceneGLES2::environment_set_bg_color(RID p_env, const Color &p_color) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->bg_color = p_color; } void RasterizerSceneGLES2::environment_set_bg_energy(RID p_env, float p_energy) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->bg_energy = p_energy; } void RasterizerSceneGLES2::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->canvas_max_layer = p_max_layer; } void RasterizerSceneGLES2::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_sky_contribution) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->ambient_color = p_color; + env->ambient_energy = p_energy; + env->ambient_sky_contribution = p_sky_contribution; } void RasterizerSceneGLES2::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_bloom_threshold, VS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, bool p_bicubic_upscale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } void RasterizerSceneGLES2::environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); } bool RasterizerSceneGLES2::is_environment(RID p_env) { - return false; + return environment_owner.owns(p_env); } VS::EnvironmentBG RasterizerSceneGLES2::environment_get_background(RID p_env) { - return VS::ENV_BG_CLEAR_COLOR; + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, VS::ENV_BG_MAX); + + return env->bg_mode; } int RasterizerSceneGLES2::environment_get_canvas_max_layer(RID p_env) { - return 0; + const Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, -1); + + return env->canvas_max_layer; } RID RasterizerSceneGLES2::light_instance_create(RID p_light) { - return RID(); + + LightInstance *light_instance = memnew(LightInstance); + + light_instance->last_scene_pass = 0; + + light_instance->light = p_light; + light_instance->light_ptr = storage->light_owner.getornull(p_light); + + ERR_FAIL_COND_V(!light_instance->light_ptr, RID()); + + light_instance->self = light_instance_owner.make_rid(light_instance); + + return light_instance->self; } void RasterizerSceneGLES2::light_instance_set_transform(RID p_light_instance, const Transform &p_transform) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; } void RasterizerSceneGLES2::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + if (light_instance->light_ptr->type != VS::LIGHT_DIRECTIONAL) { + p_pass = 0; + } + + ERR_FAIL_INDEX(p_pass, 4); + + light_instance->shadow_transform[p_pass].camera = p_projection; + light_instance->shadow_transform[p_pass].transform = p_transform; + light_instance->shadow_transform[p_pass].farplane = p_far; + light_instance->shadow_transform[p_pass].split = p_split; + light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale; } void RasterizerSceneGLES2::light_instance_mark_visible(RID p_light_instance) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->last_scene_pass = scene_pass; } ////////////////////// @@ -206,13 +663,1552 @@ void RasterizerSceneGLES2::gi_probe_instance_set_bounds(RID p_probe, const Vecto //////////////////////////// //////////////////////////// +void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass) { + + RasterizerStorageGLES2::Material *material = NULL; + RID material_src; + + if (p_instance->material_override.is_valid()) { + material_src = p_instance->material_override; + } else if (p_material >= 0) { + material_src = p_instance->materials[p_material]; + } else { + material_src = p_geometry->material; + } + + if (material_src.is_valid()) { + material = storage->material_owner.getornull(material_src); + + if (!material->shader || !material->shader->valid) { + material = NULL; + } + } + + if (!material) { + material = storage->material_owner.getptr(default_material); + } + + ERR_FAIL_COND(!material); + + _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass); + + while (material->next_pass.is_valid()) { + material = storage->material_owner.getornull(material->next_pass); + + if (!material || !material->shader || !material->shader->valid) { + break; + } + + _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass); + } +} +void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass) { + + bool has_base_alpha = (p_material->shader->spatial.uses_alpha && !p_material->shader->spatial.uses_alpha_scissor) || p_material->shader->spatial.uses_screen_texture || p_material->shader->spatial.uses_depth_texture; + bool has_blend_alpha = p_material->shader->spatial.blend_mode != RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX; + bool has_alpha = has_base_alpha || has_blend_alpha; + + // TODO add this stuff + // bool mirror = p_instance->mirror; + // bool no_cull = false; + + RenderList::Element *e = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); + + if (!e) { + return; + } + + e->geometry = p_geometry; + e->material = p_material; + e->instance = p_instance; + e->owner = p_owner; + e->sort_key = 0; + + // TODO check render pass of geometry + + // TODO check directional light flag + + if (p_depth_pass) { + // if we are in the depth pass we can sort out a few things to improve performance + + if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { + return; + } + + if (p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) { + + // shader doesn't use discard or writes a custom vertex position, + // so we can use a stripped down shader instead + + // TODO twosided and worldcoord stuff + + p_material = storage->material_owner.getptr(default_material_twosided); + } + + has_alpha = false; + } + + e->sort_key |= uint64_t(e->geometry->index) << RenderList::SORT_KEY_GEOMETRY_INDEX_SHIFT; + e->sort_key |= uint64_t(e->instance->base_type) << RenderList::SORT_KEY_GEOMETRY_TYPE_SHIFT; + + if (p_material->shader->spatial.unshaded) { + e->sort_key |= SORT_KEY_UNSHADED_FLAG; + } + + if (!p_depth_pass) { + e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT; + + e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT; + } else { + // TODO + } + + if (p_material->shader->spatial.uses_time) { + VisualServerRaster::redraw_request(); + } +} + +void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass) { + + for (int i = 0; i < p_cull_count; i++) { + + InstanceBase *instance = p_cull_result[i]; + + switch (instance->base_type) { + + case VS::INSTANCE_MESH: { + + RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getornull(instance->base); + ERR_CONTINUE(!mesh); + + int num_surfaces = mesh->surfaces.size(); + + for (int i = 0; i < num_surfaces; i++) { + int material_index = instance->materials[i].is_valid() ? i : -1; + + RasterizerStorageGLES2::Surface *surface = mesh->surfaces[i]; + + _add_geometry(surface, instance, NULL, material_index, p_depth_pass, p_shadow_pass); + } + + } break; + + case VS::INSTANCE_MULTIMESH: { + RasterizerStorageGLES2::MultiMesh *multi_mesh = storage->multimesh_owner.getptr(instance->base); + ERR_CONTINUE(!multi_mesh); + + if (multi_mesh->size == 0 || multi_mesh->visible_instances == 0) + continue; + + RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getptr(multi_mesh->mesh); + if (!mesh) + continue; + + int ssize = mesh->surfaces.size(); + + for (int i = 0; i < ssize; i++) { + RasterizerStorageGLES2::Surface *s = mesh->surfaces[i]; + _add_geometry(s, instance, multi_mesh, -1, p_depth_pass, p_shadow_pass); + } + } break; + + default: { + + } break; + } + } +} + +static const GLenum gl_primitive[] = { + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN +}; + +void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, Size2i p_skeleton_tex_size) { + + // material parameters + + state.scene_shader.set_custom_shader(p_material->shader->custom_code_id); + + state.scene_shader.bind(); + + if (p_material->shader->spatial.no_depth_test) { + glDisable(GL_DEPTH_TEST); + } else { + glEnable(GL_DEPTH_TEST); + } + + // TODO whyyyyy???? + p_reverse_cull = true; + + switch (p_material->shader->spatial.cull_mode) { + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED: { + glDisable(GL_CULL_FACE); + } break; + + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_BACK: { + glEnable(GL_CULL_FACE); + glCullFace(p_reverse_cull ? GL_FRONT : GL_BACK); + } break; + case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT: { + glEnable(GL_CULL_FACE); + glCullFace(p_reverse_cull ? GL_BACK : GL_FRONT); + } break; + } + + int tc = p_material->textures.size(); + Pair<StringName, RID> *textures = p_material->textures.ptrw(); + + ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw(); + + state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size); + + for (int i = 0; i < tc; i++) { + + glActiveTexture(GL_TEXTURE0 + i); + + RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second); + + if (!t) { + + switch (texture_hints[i]) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + } break; + default: { + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + } + + continue; + } + + t = t->get_ptr(); + + glBindTexture(t->target, t->tex_id); + } + state.scene_shader.use_material((void *)p_material); +} + +void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) { + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, p_skeleton != NULL); + // state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, true); + + switch (p_element->instance->base_type) { + + case VS::INSTANCE_MESH: { + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, s->attribs[VS::ARRAY_COLOR].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled); + + } break; + + case VS::INSTANCE_MULTIMESH: { + RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, true); + + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled); + state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled); + } break; + + default: { + + } break; + } + + if (false && storage->config.float_texture_supported) { + if (p_skeleton) { + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id); + } + + return; + } + + if (p_skeleton) { + ERR_FAIL_COND(p_skeleton->use_2d); + + PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer; + + switch (p_element->instance->base_type) { + case VS::INSTANCE_MESH: { + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + if (!s->attribs[VS::ARRAY_BONES].enabled || !s->attribs[VS::ARRAY_WEIGHTS].enabled) { + break; // the whole instance has a skeleton, but this surface is not affected by it. + } + + // 3 * vec4 per vertex + if (transform_buffer.size() < s->array_len * 12) { + transform_buffer.resize(s->array_len * 12); + } + + const size_t bones_offset = s->attribs[VS::ARRAY_BONES].offset; + const size_t bones_stride = s->attribs[VS::ARRAY_BONES].stride; + const size_t bone_weight_offset = s->attribs[VS::ARRAY_WEIGHTS].offset; + const size_t bone_weight_stride = s->attribs[VS::ARRAY_WEIGHTS].stride; + + { + PoolVector<float>::Write write = transform_buffer.write(); + float *buffer = write.ptr(); + + PoolVector<uint8_t>::Read vertex_array_read = s->data.read(); + const uint8_t *vertex_data = vertex_array_read.ptr(); + + for (int i = 0; i < s->array_len; i++) { + + // do magic + + size_t bones[4]; + float bone_weight[4]; + + if (s->attribs[VS::ARRAY_BONES].type == GL_UNSIGNED_BYTE) { + // read as byte + const uint8_t *bones_ptr = vertex_data + bones_offset + (i * bones_stride); + bones[0] = bones_ptr[0]; + bones[1] = bones_ptr[1]; + bones[2] = bones_ptr[2]; + bones[3] = bones_ptr[3]; + } else { + // read as short + const uint16_t *bones_ptr = (const uint16_t *)(vertex_data + bones_offset + (i * bones_stride)); + bones[0] = bones_ptr[0]; + bones[1] = bones_ptr[1]; + bones[2] = bones_ptr[2]; + bones[3] = bones_ptr[3]; + } + + if (s->attribs[VS::ARRAY_WEIGHTS].type == GL_FLOAT) { + // read as float + const float *weight_ptr = (const float *)(vertex_data + bone_weight_offset + (i * bone_weight_stride)); + bone_weight[0] = weight_ptr[0]; + bone_weight[1] = weight_ptr[1]; + bone_weight[2] = weight_ptr[2]; + bone_weight[3] = weight_ptr[3]; + } else { + // read as half + const uint16_t *weight_ptr = (const uint16_t *)(vertex_data + bone_weight_offset + (i * bone_weight_stride)); + bone_weight[0] = (weight_ptr[0] / (float)0xFFFF); + bone_weight[1] = (weight_ptr[1] / (float)0xFFFF); + bone_weight[2] = (weight_ptr[2] / (float)0xFFFF); + bone_weight[3] = (weight_ptr[3] / (float)0xFFFF); + } + + size_t offset = i * 12; + + Transform transform; + + Transform bone_transforms[4] = { + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[0]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[1]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[2]), + storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[3]), + }; + + transform.origin = + bone_weight[0] * bone_transforms[0].origin + + bone_weight[1] * bone_transforms[1].origin + + bone_weight[2] * bone_transforms[2].origin + + bone_weight[3] * bone_transforms[3].origin; + + transform.basis = + bone_transforms[0].basis * bone_weight[0] + + bone_transforms[1].basis * bone_weight[1] + + bone_transforms[2].basis * bone_weight[2] + + bone_transforms[3].basis * bone_weight[3]; + + float row[3][4] = { + { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] }, + { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] }, + { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] }, + }; + + size_t transform_buffer_offset = i * 12; + + copymem(&buffer[transform_buffer_offset], row, sizeof(row)); + } + } + + storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12); + } break; + + default: { + + } break; + } + } +} + +void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { + + switch (p_element->instance->base_type) { + + case VS::INSTANCE_MESH: { + + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + // set up + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glEnableVertexAttribArray(VS::ARRAY_MAX + 0); + glEnableVertexAttribArray(VS::ARRAY_MAX + 1); + glEnableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); + } else { + // just to make sure + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + if (s->attribs[i].enabled) { + glEnableVertexAttribArray(i); + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset); + } else { + glDisableVertexAttribArray(i); + } + } + + // drawing + + if (s->index_array_len > 0) { + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + } else { + glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + } + + // tear down + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + } break; + + case VS::INSTANCE_MULTIMESH: { + + RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); + RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); + + int amount = MIN(multi_mesh->size, multi_mesh->visible_instances); + if (amount == -1) { + amount = multi_mesh->size; + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glEnableVertexAttribArray(VS::ARRAY_MAX + 0); + glEnableVertexAttribArray(VS::ARRAY_MAX + 1); + glEnableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); + } else { + // just to make sure + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + + glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); + glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id); + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + if (s->attribs[i].enabled) { + glEnableVertexAttribArray(i); + glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, (uint8_t *)0 + s->attribs[i].offset); + } else { + glDisableVertexAttribArray(i); + } + } + + glDisableVertexAttribArray(12); // transform 0 + glDisableVertexAttribArray(13); // transform 1 + glDisableVertexAttribArray(14); // transform 2 + glDisableVertexAttribArray(15); // color + glDisableVertexAttribArray(8); // custom data + + glVertexAttrib4f(15, 1, 1, 1, 1); + glVertexAttrib4f(8, 0, 0, 0, 0); + + int stride = multi_mesh->color_floats + multi_mesh->custom_data_floats + multi_mesh->xform_floats; + + int color_ofs = multi_mesh->xform_floats; + int custom_data_ofs = color_ofs + multi_mesh->color_floats; + + // drawing + + for (int i = 0; i < amount; i++) { + float *buffer = &multi_mesh->data.write[i * stride]; + + { + // inline of multimesh_get_transform since it's such a pain + // to get a RID from here... + Transform transform; + + transform.basis.elements[0][0] = buffer[0]; + transform.basis.elements[0][1] = buffer[1]; + transform.basis.elements[0][2] = buffer[2]; + transform.origin.x = buffer[3]; + transform.basis.elements[1][0] = buffer[4]; + transform.basis.elements[1][1] = buffer[5]; + transform.basis.elements[1][2] = buffer[6]; + transform.origin.y = buffer[7]; + transform.basis.elements[2][0] = buffer[8]; + transform.basis.elements[2][1] = buffer[9]; + transform.basis.elements[2][2] = buffer[10]; + transform.origin.z = buffer[11]; + + float row[3][4] = { + { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] }, + { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] }, + { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] }, + }; + + glVertexAttrib4fv(12, row[0]); + glVertexAttrib4fv(13, row[1]); + glVertexAttrib4fv(14, row[2]); + } + + if (multi_mesh->color_floats) { + glVertexAttrib4fv(15, buffer + color_ofs); + } + + if (multi_mesh->custom_data_floats) { + glVertexAttrib4fv(8, buffer + custom_data_ofs); + } + + if (s->index_array_len > 0) { + glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0); + } else { + glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); + } + } + + // tear down + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + + if (s->index_array_len > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { + glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); + + glDisableVertexAttribArray(VS::ARRAY_MAX + 0); + glDisableVertexAttribArray(VS::ARRAY_MAX + 1); + glDisableVertexAttribArray(VS::ARRAY_MAX + 2); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } break; + } +} + +void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_count, const RID *p_directional_lights, int p_directional_light_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RID p_shadow_atlas, Environment *p_env, GLuint p_base_env, float p_shadow_bias, float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + Vector2 screen_pixel_size; + screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + + bool use_radiance_map = false; + + VMap<RID, Vector<RenderList::Element *> > lit_objects; + + for (int i = 0; i < p_element_count; i++) { + RenderList::Element *e = p_elements[i]; + + RasterizerStorageGLES2::Material *material = e->material; + + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + if (p_base_env) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_CUBE_MAP, p_base_env); + use_radiance_map = true; + } + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map); + + if (material->shader->spatial.unshaded) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + } else { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map); + } + + // opaque pass + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + + _setup_geometry(e, skeleton); + + _setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + + if (use_radiance_map) { + state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform); + } + + if (p_shadow) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_NORMAL_BIAS, p_shadow_normal_bias); + } + + if (p_env) { + state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, p_env->ambient_energy); + + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, Color(1.0, 1.0, 1.0, 1.0)); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0); + } + + glEnable(GL_BLEND); + + if (p_alpha_pass || p_directional_add) { + int desired_blend_mode; + if (p_directional_add) { + desired_blend_mode = RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD; + } else { + desired_blend_mode = material->shader->spatial.blend_mode; + } + + switch (desired_blend_mode) { + + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE); + + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: { + + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } break; + case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); + } else { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); + } + + } break; + } + } else { + // no blend mode given - assume mix + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + + _render_geometry(e); + + if (material->shader->spatial.unshaded) + continue; + + if (p_shadow) + continue; + + for (int light = 0; light < e->instance->light_instances.size(); light++) { + + RID light_instance = e->instance->light_instances[light]; + + lit_objects[light_instance].push_back(e); + } + } + + if (p_shadow) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); + return; + } + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, true); + + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + for (int lo = 0; lo < lit_objects.size(); lo++) { + + RID key = lit_objects.getk(lo); + + LightInstance *light = light_instance_owner.getornull(key); + RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; + + const Vector<RenderList::Element *> &list = lit_objects.getv(lo); + + for (int i = 0; i < list.size(); i++) { + + RenderList::Element *e = list[i]; + RasterizerStorageGLES2::Material *material = e->material; + + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + { + _setup_geometry(e, skeleton); + + _setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + if (shadow_atlas != NULL) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + } + + switch (light_ptr->type) { + case VS::LIGHT_OMNI: { + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)1); + + Vector3 position = p_view_transform.inverse().xform(light->transform.origin); + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); + + float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); + + Color attenuation = Color(0.0, 0.0, 0.0, 0.0); + attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); + + if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) { + + uint32_t key = shadow_atlas->shadow_owners[light->self]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; + + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + uint32_t width = shadow_size; + uint32_t height = shadow_size; + + if (light->light_ptr->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + height /= 2; + } else { + width /= 2; + } + + Transform proj = (p_view_transform.inverse() * light->transform).inverse(); + + Color light_clamp; + light_clamp[0] = float(x) / atlas_size; + light_clamp[1] = float(y) / atlas_size; + light_clamp[2] = float(width) / atlas_size; + light_clamp[3] = float(height) / atlas_size; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + } break; + + case VS::LIGHT_SPOT: { + Vector3 position = p_view_transform.inverse().xform(light->transform.origin); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)2); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position); + + Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); + Color attenuation = Color(0.0, 0.0, 0.0, 0.0); + attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; + float spot_attenuation = light_ptr->param[VS::LIGHT_PARAM_SPOT_ATTENUATION]; + float angle = light_ptr->param[VS::LIGHT_PARAM_SPOT_ANGLE]; + angle = Math::cos(Math::deg2rad(angle)); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ATTENUATION, spot_attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_RANGE, spot_attenuation); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); + + if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { + uint32_t key = shadow_atlas->shadow_owners[light->self]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t atlas_size = shadow_atlas->size; + uint32_t quadrant_size = atlas_size >> 1; + + uint32_t x = (quadrant & 1) * quadrant_size; + uint32_t y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + uint32_t width = shadow_size; + uint32_t height = shadow_size; + + Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size); + + Color light_clamp; + light_clamp[0] = rect.position.x; + light_clamp[1] = rect.position.y; + light_clamp[2] = rect.size.x; + light_clamp[3] = rect.size.y; + + Transform modelview = (p_view_transform.inverse() * light->transform).inverse(); + + CameraMatrix bias; + bias.set_light_bias(); + + CameraMatrix rectm; + rectm.set_light_atlas_rect(rect); + + CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, shadow_matrix); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + + } break; + + default: break; + } + + float energy = light->light_ptr->param[VS::LIGHT_PARAM_ENERGY]; + float specular = light->light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, light->light_ptr->color.to_linear()); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + + _render_geometry(e); + } + } + + for (int dl = 0; dl < p_directional_light_count; dl++) { + RID light_rid = p_directional_lights[dl]; + LightInstance *light = light_instance_owner.getornull(light_rid); + RasterizerStorageGLES2::Light *light_ptr = light->light_ptr; + + switch (light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + } break; + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, true); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits); + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, true); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits); + } break; + default: + break; + } + + for (int i = 0; i < p_element_count; i++) { + + RenderList::Element *e = p_elements[i]; + RasterizerStorageGLES2::Material *material = e->material; + RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton); + + { + _setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); + + if (directional_shadow.depth) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); // TODO move into base pass + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse()); + state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse()); + + state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]); + + state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size); + state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + } + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)0); + Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); + + float energy = light_ptr->param[VS::LIGHT_PARAM_ENERGY]; + float specular = light_ptr->param[VS::LIGHT_PARAM_SPECULAR]; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular); + + float sign = light_ptr->negative ? -1 : 1; + + Color linear_col = light_ptr->color.to_linear(); + Color color; + for (int c = 0; c < 3; c++) + color[c] = linear_col[c] * sign * energy * Math_PI; + + color[3] = 0; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color); + + CameraMatrix matrices[4]; + + if (light_ptr->shadow && directional_shadow.depth) { + + int shadow_count = 0; + Color split_offsets; + + switch (light_ptr->directional_shadow_mode) { + case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: { + shadow_count = 1; + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: { + shadow_count = 2; + } break; + + case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: { + shadow_count = 4; + } break; + } + + for (int k = 0; k < shadow_count; k++) { + + uint32_t x = light->directional_rect.position.x; + uint32_t y = light->directional_rect.position.y; + uint32_t width = light->directional_rect.size.x; + uint32_t height = light->directional_rect.size.y; + + if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + + width /= 2; + height /= 2; + + if (k == 0) { + + } else if (k == 1) { + x += width; + } else if (k == 2) { + y += height; + } else if (k == 3) { + x += width; + y += height; + } + + } else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + + height /= 2; + + if (k == 0) { + + } else { + y += height; + } + } + + split_offsets[k] = light->shadow_transform[k].split; + + Transform modelview = (p_view_transform * light->shadow_transform[k].transform).inverse(); + + CameraMatrix bias; + bias.set_light_bias(); + CameraMatrix rectm; + Rect2 atlas_rect = Rect2(float(x) / directional_shadow.size, float(y) / directional_shadow.size, float(width) / directional_shadow.size, float(height) / directional_shadow.size); + rectm.set_light_atlas_rect(atlas_rect); + + CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview; + matrices[k] = shadow_mtx.inverse(); + + Color light_clamp; + light_clamp[0] = atlas_rect.position.x; + light_clamp[1] = atlas_rect.position.y; + light_clamp[2] = atlas_rect.size.x; + light_clamp[3] = atlas_rect.size.y; + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets); + } + + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX1, matrices[0]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX2, matrices[1]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX3, matrices[2]); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX4, matrices[3]); + } else { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0); + } + + _render_geometry(e); + } + } + + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false); + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); +} + +void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) { + ERR_FAIL_COND(!p_sky); + + RasterizerStorageGLES2::Texture *tex = storage->texture_owner.getornull(p_sky->panorama); + ERR_FAIL_COND(!tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(tex->target, tex->tex_id); + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + + // Camera + CameraMatrix camera; + + if (p_custom_fov) { + + float near_plane = p_projection.get_z_near(); + float far_plane = p_projection.get_z_far(); + float aspect = p_projection.get_aspect(); + + camera.set_perspective(p_custom_fov, aspect, near_plane, far_plane); + } else { + camera = p_projection; + } + + float flip_sign = p_vflip ? -1 : 1; + + // If matrix[2][0] or matrix[2][1] we're dealing with an asymmetrical projection matrix. This is the case for stereoscopic rendering (i.e. VR). + // To ensure the image rendered is perspective correct we need to move some logic into the shader. For this the USE_ASYM_PANO option is introduced. + // It also means the uv coordinates are ignored in this mode and we don't need our loop. + bool asymmetrical = ((camera.matrix[2][0] != 0.0) || (camera.matrix[2][1] != 0.0)); + + Vector3 vertices[8] = { + Vector3(-1, -1 * flip_sign, 1), + Vector3(0, 1, 0), + Vector3(1, -1 * flip_sign, 1), + Vector3(1, 1, 0), + Vector3(1, 1 * flip_sign, 1), + Vector3(1, 0, 0), + Vector3(-1, 1 * flip_sign, 1), + Vector3(0, 0, 0), + }; + + if (!asymmetrical) { + float vw, vh, zn; + camera.get_viewport_size(vw, vh); + zn = p_projection.get_z_near(); + + for (int i = 0; i < 4; i++) { + Vector3 uv = vertices[i * 2 + 1]; + uv.x = (uv.x * 2.0 - 1.0) * vw; + uv.y = -(uv.y * 2.0 - 1.0) * vh; + uv.z = -zn; + vertices[i * 2 + 1] = p_transform.basis.xform(uv).normalized(); + vertices[i * 2 + 1].z = -vertices[i * 2 + 1].z; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector3) * 8, vertices); + + // bind sky vertex array.... + glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, 0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, ((uint8_t *)NULL) + sizeof(Vector3)); + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, true); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, true); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false); + storage->shaders.copy.bind(); + storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisableVertexAttribArray(VS::ARRAY_VERTEX); + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); +} + void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { + + glEnable(GL_BLEND); + + GLuint current_fb = storage->frame.current_rt->fbo; + Environment *env = environment_owner.getornull(p_environment); + + // render list stuff + + render_list.clear(); + _fill_render_list(p_cull_result, p_cull_count, false, false); + + // other stuff + + glBindFramebuffer(GL_FRAMEBUFFER, current_fb); + + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + storage->frame.clear_request = false; + + glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // render sky + RasterizerStorageGLES2::Sky *sky = NULL; + GLuint env_radiance_tex = 0; + if (env) { + switch (env->bg_mode) { + + case VS::ENV_BG_COLOR_SKY: + case VS::ENV_BG_SKY: { + sky = storage->sky_owner.getornull(env->sky); + + if (sky) { + env_radiance_tex = sky->radiance; + } + } break; + + default: { + print_line("uhm"); + } break; + } + } + + if (env && env->bg_mode == VS::ENV_BG_SKY && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) { + + if (sky && sky->panorama.is_valid()) { + _draw_sky(sky, p_cam_projection, p_cam_transform, false, env->sky_custom_fov, env->bg_energy); + } + } + + Vector<RID> directional_lights; + + for (int i = 0; i < p_light_cull_count; i++) { + RID light_rid = p_light_cull_result[i]; + + LightInstance *light = light_instance_owner.getornull(light_rid); + + if (light->light_ptr->type == VS::LIGHT_DIRECTIONAL) { + directional_lights.push_back(light_rid); + } + } + + // render opaque things first + render_list.sort_by_key(false); + _render_render_list(render_list.elements, render_list.element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, false, false, false); + + // alpha pass + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + render_list.sort_by_key(true); + _render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, true, false, false); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + + // #define GLES2_SHADOW_ATLAS_DEBUG_VIEW + +#ifdef GLES2_SHADOW_ATLAS_DEBUG_VIEW + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + if (shadow_atlas) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); + + glViewport(0, 0, storage->frame.current_rt->width / 4, storage->frame.current_rt->height / 4); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false); + storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false); + storage->shaders.copy.bind(); + + storage->_copy_screen(); + } +#endif } void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) { + + LightInstance *light_instance = light_instance_owner.getornull(p_light); + ERR_FAIL_COND(!light_instance); + + RasterizerStorageGLES2::Light *light = light_instance->light_ptr; + ERR_FAIL_COND(!light); + + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t vp_height; + + float zfar = 0; + bool flip_facing = false; + int custom_vp_size = 0; + + GLuint fbo = 0; + + int current_cubemap = -1; + float bias = 0; + float normal_bias = 0; + + CameraMatrix light_projection; + Transform light_transform; + + // TODO directional light + + if (light->type == VS::LIGHT_DIRECTIONAL) { + // set pssm stuff + + // TODO set this only when changed + + light_instance->light_directional_index = directional_shadow.current_light; + light_instance->last_scene_shadow_pass = scene_pass; + + directional_shadow.current_light++; + + if (directional_shadow.light_count == 1) { + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size); + } else if (directional_shadow.light_count == 2) { + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2); + if (light_instance->light_directional_index == 1) { + light_instance->directional_rect.position.x += light_instance->directional_rect.size.x; + } + } else { //3 and 4 + light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2); + if (light_instance->light_directional_index & 1) { + light_instance->directional_rect.position.x += light_instance->directional_rect.size.x; + } + if (light_instance->light_directional_index / 2) { + light_instance->directional_rect.position.y += light_instance->directional_rect.size.y; + } + } + + light_projection = light_instance->shadow_transform[p_pass].camera; + light_transform = light_instance->shadow_transform[p_pass].transform; + + x = light_instance->directional_rect.position.x; + y = light_instance->directional_rect.position.y; + width = light_instance->directional_rect.size.width; + height = light_instance->directional_rect.size.height; + + if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { + + width /= 2; + height /= 2; + + if (p_pass == 0) { + + } else if (p_pass == 1) { + x += width; + } else if (p_pass == 2) { + y += height; + } else if (p_pass == 3) { + x += width; + y += height; + } + + } else if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { + + height /= 2; + + if (p_pass == 0) { + + } else { + y += height; + } + } + + float bias_mult = Math::lerp(1.0f, light_instance->shadow_transform[p_pass].bias_scale, light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE]); + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS] * bias_mult; + normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult; + + fbo = directional_shadow.fbo; + vp_height = directional_shadow.size; + } else { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + ERR_FAIL_COND(!shadow_atlas); + ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light)); + + fbo = shadow_atlas->fbo; + vp_height = shadow_atlas->size; + + uint32_t key = shadow_atlas->shadow_owners[p_light]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_FAIL_INDEX((int)shadow, shadow_atlas->quadrants[quadrant].shadows.size()); + + uint32_t quadrant_size = shadow_atlas->size >> 1; + + x = (quadrant & 1) * quadrant_size; + y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + width = shadow_size; + height = shadow_size; + + if (light->type == VS::LIGHT_OMNI) { + // cubemap only + if (light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE) { + int cubemap_index = shadow_cubemaps.size() - 1; + + // find an appropriate cubemap to render to + for (int i = shadow_cubemaps.size() - 1; i >= 0; i--) { + if (shadow_cubemaps[i].size > shadow_size * 2) { + break; + } + + cubemap_index = i; + } + + fbo = shadow_cubemaps[cubemap_index].fbo[p_pass]; + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + custom_vp_size = shadow_cubemaps[cubemap_index].size; + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + + current_cubemap = cubemap_index; + } + } else { + light_projection = light_instance->shadow_transform[0].camera; + light_transform = light_instance->shadow_transform[0].transform; + + flip_facing = false; + zfar = light->param[VS::LIGHT_PARAM_RANGE]; + bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS]; + normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS]; + } + } + + render_list.clear(); + + _fill_render_list(p_cull_result, p_cull_count, true, true); + + render_list.sort_by_depth(false); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glEnable(GL_DEPTH_TEST); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glDepthMask(GL_TRUE); + glColorMask(0, 0, 0, 0); + + if (custom_vp_size) { + glViewport(0, 0, custom_vp_size, custom_vp_size); + glScissor(0, 0, custom_vp_size, custom_vp_size); + } else { + glViewport(x, y, width, height); + glScissor(x, y, width, height); + } + + glEnable(GL_SCISSOR_TEST); + glClearDepth(1.0f); + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, true); + + _render_render_list(render_list.elements, render_list.element_count, NULL, 0, light_transform, light_projection, RID(), NULL, 0, bias, normal_bias, false, false, true, false); + + state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, false); + + // convert cubemap to dual paraboloid if needed + if (light->type == VS::LIGHT_OMNI && light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE && p_pass == 5) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo); + state.cube_to_dp_shader.bind(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, shadow_cubemaps[current_cubemap].cubemap); + + glDisable(GL_CULL_FACE); + + for (int i = 0; i < 2; i++) { + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FLIP, i == 1); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_NEAR, light_projection.get_z_near()); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FAR, light_projection.get_z_far()); + state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::BIAS, light->param[VS::LIGHT_PARAM_SHADOW_BIAS]); + + uint32_t local_width = width; + uint32_t local_height = height; + uint32_t local_x = x; + uint32_t local_y = y; + + if (light->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) { + local_height /= 2; + local_y += i * local_height; + } else { + local_width /= 2; + local_x += i * local_width; + } + + glViewport(local_x, local_y, local_width, local_height); + glScissor(local_x, local_y, local_width, local_height); + + glEnable(GL_SCISSOR_TEST); + + glClearDepth(1.0f); + + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + + glDisable(GL_BLEND); + + storage->_copy_screen(); + } + } + + glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); } void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) { + scene_pass = p_pass; } bool RasterizerSceneGLES2::free(RID p_rid) { @@ -223,6 +2219,100 @@ void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_dra } void RasterizerSceneGLES2::initialize() { + state.scene_shader.init(); + state.cube_to_dp_shader.init(); + + render_list.init(); + + shadow_atlas_realloc_tolerance_msec = 500; + + { + //default material and shader + + default_shader = storage->shader_create(); + storage->shader_set_code(default_shader, "shader_type spatial;\n"); + default_material = storage->material_create(); + storage->material_set_shader(default_material, default_shader); + + default_shader_twosided = storage->shader_create(); + default_material_twosided = storage->material_create(); + storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n"); + storage->material_set_shader(default_material_twosided, default_shader_twosided); + } + + { + glGenBuffers(1, &state.sky_verts); + glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // cubemaps for shadows + { + int max_shadow_cubemap_sampler_size = 512; + + int cube_size = max_shadow_cubemap_sampler_size; + + glActiveTexture(GL_TEXTURE0); + + while (cube_size >= 32) { + + ShadowCubeMap cube; + + cube.size = cube_size; + + glGenTextures(1, &cube.cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap); + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT16, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL); + } + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(6, cube.fbo); + for (int i = 0; i < 6; i++) { + + glBindFramebuffer(GL_FRAMEBUFFER, cube.fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _cube_side_enum[i], cube.cubemap, 0); + } + + shadow_cubemaps.push_back(cube); + + cube_size >>= 1; + } + } + + { + // directional shadows + + directional_shadow.light_count = 0; + directional_shadow.size = next_power_of_2(GLOBAL_GET("rendering/quality/directional_shadow/size")); + + glGenFramebuffers(1, &directional_shadow.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo); + + glGenTextures(1, &directional_shadow.depth); + glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + ERR_PRINT("Directional shadow framebuffer status invalid"); + } + } } void RasterizerSceneGLES2::iteration() { diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index 110222f709..f47d1f1d4e 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -33,10 +33,10 @@ /* Must come before shaders or the Windows build fails... */ #include "rasterizer_storage_gles2.h" +#include "shaders/cube_to_dp.glsl.gen.h" #include "shaders/scene.glsl.gen.h" /* -#include "drivers/gles3/shaders/cube_to_dp.glsl.gen.h" #include "drivers/gles3/shaders/effect_blur.glsl.gen.h" #include "drivers/gles3/shaders/exposure.glsl.gen.h" #include "drivers/gles3/shaders/resolve.glsl.gen.h" @@ -52,6 +52,13 @@ class RasterizerSceneGLES2 : public RasterizerScene { public: + RID default_material; + RID default_material_twosided; + RID default_shader; + RID default_shader_twosided; + + uint64_t scene_pass; + RasterizerStorageGLES2 *storage; struct State { @@ -63,7 +70,10 @@ public: GLuint current_main_tex; SceneShaderGLES2 scene_shader; - // CubeToDpShaderGLES3 cube_to_dp_shader; + CubeToDpShaderGLES2 cube_to_dp_shader; + + GLuint sky_verts; + // ResolveShaderGLES3 resolve_shader; // ScreenSpaceReflectionShaderGLES3 ssr_shader; // EffectBlurShaderGLES3 effect_blur_shader; @@ -128,7 +138,6 @@ public: GLuint env_radiance_ubo; - GLuint sky_verts; GLuint sky_array; GLuint directional_ubo; @@ -169,11 +178,72 @@ public: /* SHADOW ATLAS API */ + uint64_t shadow_atlas_realloc_tolerance_msec; + + struct ShadowAtlas : public RID_Data { + enum { + QUADRANT_SHIFT = 27, + SHADOW_INDEX_MASK = (1 << QUADRANT_SHIFT) - 1, + SHADOW_INVALID = 0xFFFFFFFF, + }; + + struct Quadrant { + uint32_t subdivision; + + struct Shadow { + RID owner; + uint64_t version; + uint64_t alloc_tick; + + Shadow() { + version = 0; + alloc_tick = 0; + } + }; + + Vector<Shadow> shadows; + + Quadrant() { + subdivision = 0; + } + } quadrants[4]; + + int size_order[4]; + uint32_t smallest_subdiv; + + int size; + + GLuint fbo; + GLuint depth; + + Map<RID, uint32_t> shadow_owners; + }; + + struct ShadowCubeMap { + GLuint fbo[6]; + GLuint cubemap; + uint32_t size; + }; + + Vector<ShadowCubeMap> shadow_cubemaps; + + RID_Owner<ShadowAtlas> shadow_atlas_owner; + RID shadow_atlas_create(); void shadow_atlas_set_size(RID p_atlas, int p_size); void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision); + bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow); bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version); + struct DirectionalShadow { + GLuint fbo; + GLuint depth; + + int light_count; + int size; + int current_light; + } directional_shadow; + virtual int get_directional_light_shadow_size(RID p_light_intance); virtual void set_directional_shadow_count(int p_count); @@ -196,6 +266,36 @@ public: virtual bool reflection_probe_instance_postprocess_step(RID p_instance); /* ENVIRONMENT API */ + + struct Environment : public RID_Data { + VS::EnvironmentBG bg_mode; + + RID sky; + float sky_custom_fov; + + Color bg_color; + float bg_energy; + float sky_ambient; + + Color ambient_color; + float ambient_energy; + float ambient_sky_contribution; + + int canvas_max_layer; + + Environment() { + bg_mode = VS::ENV_BG_CLEAR_COLOR; + sky_custom_fov = 0.0; + bg_energy = 1.0; + sky_ambient = 0; + ambient_energy = 1.0; + ambient_sky_contribution = 0.0; + canvas_max_layer = 0; + } + }; + + mutable RID_Owner<Environment> environment_owner; + virtual RID environment_create(); virtual void environment_set_background(RID p_env, VS::EnvironmentBG p_bg); @@ -228,6 +328,43 @@ public: virtual int environment_get_canvas_max_layer(RID p_env); /* LIGHT INSTANCE */ + + struct LightInstance : public RID_Data { + + struct ShadowTransform { + CameraMatrix camera; + Transform transform; + float farplane; + float split; + float bias_scale; + }; + + ShadowTransform shadow_transform[4]; + + RID self; + RID light; + + RasterizerStorageGLES2::Light *light_ptr; + Transform transform; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att; + + // TODO passes and all that stuff ? + uint64_t last_scene_pass; + uint64_t last_scene_shadow_pass; + + uint16_t light_index; + uint16_t light_directional_index; + + Rect2 directional_rect; + + Set<RID> shadow_atlases; // atlases where this light is registered + }; + + mutable RID_Owner<LightInstance> light_instance_owner; + virtual RID light_instance_create(RID p_light); virtual void light_instance_set_transform(RID p_light_instance, const Transform &p_transform); virtual void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale = 1.0); @@ -242,6 +379,192 @@ public: /* RENDER LIST */ + struct RenderList { + enum { + DEFAULT_MAX_ELEMENTS = 65536, + SORT_FLAG_SKELETON = 1, + SORT_FLAG_INSTANCING = 2, + MAX_DIRECTIONAL_LIGHTS = 16, + MAX_LIGHTS = 4096, + MAX_REFLECTIONS = 1024, + + SORT_KEY_PRIORITY_SHIFT = 56, + SORT_KEY_PRIORITY_MASK = 0xFF, + //depth layer for opaque (56-52) + SORT_KEY_OPAQUE_DEPTH_LAYER_SHIFT = 52, + SORT_KEY_OPAQUE_DEPTH_LAYER_MASK = 0xF, +//64 bits unsupported in MSVC +#define SORT_KEY_UNSHADED_FLAG (uint64_t(1) << 49) +#define SORT_KEY_NO_DIRECTIONAL_FLAG (uint64_t(1) << 48) +#define SORT_KEY_LIGHTMAP_CAPTURE_FLAG (uint64_t(1) << 47) +#define SORT_KEY_LIGHTMAP_FLAG (uint64_t(1) << 46) +#define SORT_KEY_GI_PROBES_FLAG (uint64_t(1) << 45) +#define SORT_KEY_VERTEX_LIT_FLAG (uint64_t(1) << 44) + SORT_KEY_SHADING_SHIFT = 44, + SORT_KEY_SHADING_MASK = 63, + //44-28 material index + SORT_KEY_MATERIAL_INDEX_SHIFT = 28, + //28-8 geometry index + SORT_KEY_GEOMETRY_INDEX_SHIFT = 8, + //bits 5-7 geometry type + SORT_KEY_GEOMETRY_TYPE_SHIFT = 5, + //bits 0-5 for flags + SORT_KEY_OPAQUE_PRE_PASS = 8, + SORT_KEY_CULL_DISABLED_FLAG = 4, + SORT_KEY_SKELETON_FLAG = 2, + SORT_KEY_MIRROR_FLAG = 1 + }; + + int max_elements; + + struct Element { + RasterizerScene::InstanceBase *instance; + + RasterizerStorageGLES2::Geometry *geometry; + RasterizerStorageGLES2::Material *material; + RasterizerStorageGLES2::GeometryOwner *owner; + + uint64_t sort_key; + }; + + Element *base_elements; + Element **elements; + + int element_count; + int alpha_element_count; + + void clear() { + element_count = 0; + alpha_element_count = 0; + } + + // sorts + + struct SortByKey { + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->sort_key < B->sort_key; + } + }; + + void sort_by_key(bool p_alpha) { + SortArray<Element *, SortByKey> sorter; + + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByDepth { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + return A->instance->depth < B->instance->depth; + } + }; + + void sort_by_depth(bool p_alpha) { //used for shadows + + SortArray<Element *, SortByDepth> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + struct SortByReverseDepthAndPriority { + + _FORCE_INLINE_ bool operator()(const Element *A, const Element *B) const { + uint32_t layer_A = uint32_t(A->sort_key >> SORT_KEY_PRIORITY_SHIFT); + uint32_t layer_B = uint32_t(B->sort_key >> SORT_KEY_PRIORITY_SHIFT); + if (layer_A == layer_B) { + return A->instance->depth > B->instance->depth; + } else { + return layer_A < layer_B; + } + } + }; + + void sort_by_reverse_depth_and_priority(bool p_alpha) { //used for alpha + + SortArray<Element *, SortByReverseDepthAndPriority> sorter; + if (p_alpha) { + sorter.sort(&elements[max_elements - alpha_element_count], alpha_element_count); + } else { + sorter.sort(elements, element_count); + } + } + + // element adding and stuff + + _FORCE_INLINE_ Element *add_element() { + if (element_count + alpha_element_count >= max_elements) + return NULL; + + elements[element_count] = &base_elements[element_count]; + return elements[element_count++]; + } + + _FORCE_INLINE_ Element *add_alpha_element() { + if (element_count + alpha_element_count >= max_elements) { + return NULL; + } + + int idx = max_elements - alpha_element_count - 1; + elements[idx] = &base_elements[idx]; + alpha_element_count++; + return elements[idx]; + } + + void init() { + element_count = 0; + alpha_element_count = 0; + + elements = memnew_arr(Element *, max_elements); + base_elements = memnew_arr(Element, max_elements); + + for (int i = 0; i < max_elements; i++) { + elements[i] = &base_elements[i]; + } + } + + RenderList() { + max_elements = DEFAULT_MAX_ELEMENTS; + } + + ~RenderList() { + memdelete_arr(elements); + memdelete_arr(base_elements); + } + }; + + RenderList render_list; + + void _add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass); + void _add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass); + + void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass); + void _render_render_list(RenderList::Element **p_elements, int p_element_count, + const RID *p_directional_lights, int p_directional_light_count, + const Transform &p_view_transform, + const CameraMatrix &p_projection, + RID p_shadow_atlas, + Environment *p_env, + GLuint p_base_env, + float p_shadow_bias, + float p_shadow_normal_bias, + bool p_reverse_cull, + bool p_alpha_pass, + bool p_shadow, + bool p_directional_add); + + void _draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy); + + void _setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, Size2i p_skeleton_tex_size = Size2i(0, 0)); + void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton); + void _render_geometry(RenderList::Element *p_element); + virtual void render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count); virtual bool free(RID p_rid); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 0dc506d991..8c4325ccde 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -32,14 +32,40 @@ #include "rasterizer_canvas_gles2.h" #include "rasterizer_scene_gles2.h" +#include "math/transform.h" + +#include "servers/visual/shader_language.h" + GLuint RasterizerStorageGLES2::system_fbo = 0; /* TEXTURE API */ -Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type) { +#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +#define _EXT_ETC1_RGB8_OES 0x8D64 + +#ifdef GLES_OVER_GL +#define _GL_HALF_FLOAT_OES 0x140B +#else +#define _GL_HALF_FLOAT_OES 0x8D61 +#endif + +void RasterizerStorageGLES2::bind_quad_array() const { + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, ((uint8_t *)NULL) + 8); + + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); +} + +Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed) { r_gl_format = 0; Ref<Image> image = p_image; + r_compressed = false; bool need_decompress = false; @@ -98,9 +124,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RF: { - ERR_EXPLAIN("R float texture not supported"); - ERR_FAIL_V(image); + if (!config.float_texture_supported) { + ERR_EXPLAIN("R float texture not supported"); + ERR_FAIL_V(image); + } + r_gl_internal_format = GL_ALPHA; + r_gl_format = GL_ALPHA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGF: { ERR_EXPLAIN("RG float texture not supported"); @@ -108,54 +139,87 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_RGBF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGB float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGB float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RGBAF: { + if (!config.float_texture_supported) { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + ERR_EXPLAIN("RGBA float texture not supported"); + ERR_FAIL_V(image); + } + + r_gl_internal_format = GL_RGBA; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; } break; case Image::FORMAT_RH: { - ERR_EXPLAIN("R half float texture not supported"); - ERR_FAIL_V(image); + need_decompress = true; } break; case Image::FORMAT_RGH: { - ERR_EXPLAIN("RG half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBH: { - ERR_EXPLAIN("RGB half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBAH: { - ERR_EXPLAIN("RGBA half float texture not supported"); - ERR_FAIL_V(image); - + need_decompress = true; } break; case Image::FORMAT_RGBE9995: { - ERR_EXPLAIN("RGBA float texture not supported"); - ERR_FAIL_V(image); + r_gl_internal_format = GL_RGB; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + + if (image.is_valid()) + + image = image->rgbe_to_srgb(); + + return image; } break; case Image::FORMAT_DXT1: { - need_decompress = true; + r_compressed = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT3: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_DXT5: { - need_decompress = true; + if (config.s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_RGTC_R: { @@ -198,7 +262,14 @@ Ref<Image> RasterizerStorageGLES2::_get_gl_image_and_format(const Ref<Image> &p_ } break; case Image::FORMAT_ETC: { - need_decompress = true; + if (config.etc1_supported) { + r_gl_internal_format = _EXT_ETC1_RGB8_OES; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } } break; case Image::FORMAT_ETC2_R11: { @@ -275,11 +346,14 @@ RID RasterizerStorageGLES2::texture_create() { return texture_owner.make_rid(texture); } -void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { +void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type, uint32_t p_flags) { GLenum format; GLenum internal_format; GLenum type; + bool compressed = false; + bool srgb = false; + if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { p_flags &= ~VS::TEXTURE_FLAG_MIPMAPS; // no mipies for video } @@ -291,9 +365,26 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->format = p_format; texture->flags = p_flags; texture->stored_cube_sides = 0; - texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + texture->type = p_type; + + switch (p_type) { + case VS::TEXTURE_TYPE_2D: { + texture->target = GL_TEXTURE_2D; + texture->images.resize(1); + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + texture->images.resize(p_depth_3d); + } break; + case VS::TEXTURE_TYPE_3D: { + texture->images.resize(p_depth_3d); + } break; + } - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type); + _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type, compressed); texture->alloc_width = texture->width; texture->alloc_height = texture->height; @@ -304,6 +395,8 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->data_size = 0; texture->mipmaps = 1; + texture->compressed = compressed; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); @@ -315,7 +408,7 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->active = true; } -void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.getornull(p_texture); ERR_FAIL_COND(!texture); @@ -328,13 +421,12 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p GLenum format; GLenum internal_format; bool compressed = false; - bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_cube_side] = p_image; + texture->images.write[p_layer] = p_image; } - Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type); + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed); if (config.shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { @@ -350,7 +442,7 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p } }; - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; texture->data_size = img->get_data().size(); PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -423,11 +515,21 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p int size, ofs; img->get_mipmap_offset_and_size(i, ofs, size); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } } tsize += size; @@ -442,9 +544,9 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p // printf("texture: %i x %i - size: %i - total: %i\n", texture->width, texture->height, tsize, info.texture_mem); - texture->stored_cube_sides |= (1 << p_cube_side); + texture->stored_cube_sides |= (1 << p_layer); - if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP) || texture->stored_cube_sides == (1 << 6) - 1)) { + if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != VS::TEXTURE_TYPE_CUBEMAP || texture->stored_cube_sides == (1 << 6) - 1)) { //generate mipmaps if they were requested and the image does not contain them glGenerateMipmap(texture->target); } @@ -452,12 +554,12 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p texture->mipmaps = mipmaps; } -void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { // TODO ERR_PRINT("Not implemented (ask Karroffel to do it :p)"); } -Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { +Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) const { Texture *texture = texture_owner.getornull(p_texture); @@ -465,8 +567,8 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSi ERR_FAIL_COND_V(!texture->active, Ref<Image>()); ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); - if (!texture->images[p_cube_side].is_null()) { - return texture->images[p_cube_side]; + if (texture->type == VS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && p_layer >= 0 && !texture->images[p_layer].is_null()) { + return texture->images[p_layer]; } #ifdef GLES_OVER_GL @@ -492,9 +594,13 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSi ofs = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->format, i - 1); } - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); + if (texture->compressed) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetCompressedTexImage(texture->target, i, &wb[ofs]); + } else { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); + } } wb = PoolVector<uint8_t>::Write(); @@ -520,8 +626,6 @@ void RasterizerStorageGLES2::texture_set_flags(RID p_texture, uint32_t p_flags) glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; - texture->flags = p_flags | cube; // can't remove a cube from being a cube if (((texture->flags & VS::TEXTURE_FLAG_REPEAT) || (texture->flags & VS::TEXTURE_FLAG_MIRRORED_REPEAT)) && texture->target != GL_TEXTURE_CUBE_MAP) { @@ -578,6 +682,14 @@ Image::Format RasterizerStorageGLES2::texture_get_format(RID p_texture) const { return texture->format; } +VisualServer::TextureType RasterizerStorageGLES2::texture_get_type(RID p_texture) const { + Texture *texture = texture_owner.getornull(p_texture); + + ERR_FAIL_COND_V(!texture, VS::TEXTURE_TYPE_2D); + + return texture->type; +} + uint32_t RasterizerStorageGLES2::texture_get_texid(RID p_texture) const { Texture *texture = texture_owner.getornull(p_texture); @@ -602,7 +714,15 @@ uint32_t RasterizerStorageGLES2::texture_get_height(RID p_texture) const { return texture->height; } -void RasterizerStorageGLES2::texture_set_size_override(RID p_texture, int p_width, int p_height) { +uint32_t RasterizerStorageGLES2::texture_get_depth(RID p_texture) const { + Texture *texture = texture_owner.getornull(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void RasterizerStorageGLES2::texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth) { Texture *texture = texture_owner.getornull(p_texture); ERR_FAIL_COND(!texture); @@ -641,8 +761,9 @@ void RasterizerStorageGLES2::texture_debug_usage(List<VS::TextureInfo> *r_info) VS::TextureInfo tinfo; tinfo.path = t->path; tinfo.format = t->format; - tinfo.size.x = t->alloc_width; - tinfo.size.y = t->alloc_height; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; tinfo.bytes = t->total_data_size; r_info->push_back(tinfo); } @@ -683,27 +804,171 @@ void RasterizerStorageGLES2::texture_set_force_redraw_if_visible(RID p_texture, } void RasterizerStorageGLES2::texture_set_detect_3d_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_3d = p_callback; + texture->detect_3d_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_srgb_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_srgb = p_callback; + texture->detect_srgb_ud = p_userdata; } void RasterizerStorageGLES2::texture_set_detect_normal_callback(RID p_texture, VisualServer::TextureDetectCallback p_callback, void *p_userdata) { - // TODO + Texture *texture = texture_owner.get(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_normal = p_callback; + texture->detect_normal_ud = p_userdata; } RID RasterizerStorageGLES2::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { - // TODO + return RID(); } RID RasterizerStorageGLES2::sky_create() { - return RID(); + Sky *sky = memnew(Sky); + sky->radiance = 0; + return sky_owner.make_rid(sky); } void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { + Sky *sky = sky_owner.getornull(p_sky); + ERR_FAIL_COND(!sky); + + if (sky->panorama.is_valid()) { + sky->panorama = RID(); + glDeleteTextures(1, &sky->radiance); + sky->radiance = 0; + } + + sky->panorama = p_panorama; + if (!sky->panorama.is_valid()) { + return; // the panorama was cleared + } + + Texture *texture = texture_owner.getornull(sky->panorama); + if (!texture) { + sky->panorama = RID(); + ERR_FAIL_COND(!texture); + } + + // glBindVertexArray(0) and more + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //need this for proper sampling + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // New cubemap that will hold the mipmaps with different roughness values + glActiveTexture(GL_TEXTURE2); + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); + + // Now we create a new framebuffer. The new cubemap images will be used as + // attachements for it, so we can fill them by issuing draw calls. + GLuint tmp_fb; + + glGenFramebuffers(1, &tmp_fb); + glBindFramebuffer(GL_FRAMEBUFFER, tmp_fb); + + int size = p_radiance_size; + + int lod = 0; + + shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, texture->target == GL_TEXTURE_2D); + + shaders.cubemap_filter.bind(); + + int mipmaps = 6; + + int mm_level = mipmaps; + + GLenum internal_format = GL_RGBA; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; // This is suboptimal... TODO other format for FBO? + + // Set the initial (empty) mipmaps + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL); + } + + lod++; + + size >>= 1; + } + + lod = 0; + mm_level = mipmaps; + + size = p_radiance_size; + + // now render to the framebuffer, mipmap level for mipmap level + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], sky->radiance, lod); + + glViewport(0, 0, size, size); + + bind_quad_array(); + + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); + + float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1; + shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + size >>= 1; + + mm_level--; + + lod++; + } + + // restore ranges + + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Framebuffer did its job. thank mr framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES2::system_fbo); + glDeleteFramebuffers(1, &tmp_fb); } /* SHADER API */ @@ -755,6 +1020,8 @@ void RasterizerStorageGLES2::shader_set_code(RID p_shader, const String &p_code) if (mode == VS::SHADER_CANVAS_ITEM) { shader->shader = &canvas->state.canvas_shader; + } else if (mode == VS::SHADER_SPATIAL) { + shader->shader = &scene->state.scene_shader; } else { return; } @@ -815,6 +1082,62 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { actions->uniforms = &p_shader->uniforms; } break; + case VS::SHADER_SPATIAL: { + p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; + p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; + p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; + p_shader->spatial.uses_alpha = false; + p_shader->spatial.uses_alpha_scissor = false; + p_shader->spatial.uses_discard = false; + p_shader->spatial.unshaded = false; + p_shader->spatial.no_depth_test = false; + p_shader->spatial.uses_sss = false; + p_shader->spatial.uses_time = false; + p_shader->spatial.uses_vertex_lighting = false; + p_shader->spatial.uses_screen_texture = false; + p_shader->spatial.uses_depth_texture = false; + p_shader->spatial.uses_vertex = false; + p_shader->spatial.writes_modelview_or_projection = false; + p_shader->spatial.uses_world_coordinates = false; + + shaders.actions_scene.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_ADD); + shaders.actions_scene.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MIX); + shaders.actions_scene.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_SUB); + shaders.actions_scene.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MUL); + + shaders.actions_scene.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_OPAQUE); + shaders.actions_scene.render_mode_values["depth_draw_always"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALWAYS); + shaders.actions_scene.render_mode_values["depth_draw_never"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_NEVER); + shaders.actions_scene.render_mode_values["depth_draw_alpha_prepass"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS); + + shaders.actions_scene.render_mode_values["cull_front"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_FRONT); + shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); + shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); + + shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; + shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; + + shaders.actions_scene.render_mode_flags["vertex_lighting"] = &p_shader->spatial.uses_vertex_lighting; + + shaders.actions_scene.render_mode_flags["world_vertex_coords"] = &p_shader->spatial.uses_world_coordinates; + + shaders.actions_scene.usage_flag_pointers["ALPHA"] = &p_shader->spatial.uses_alpha; + shaders.actions_scene.usage_flag_pointers["ALPHA_SCISSOR"] = &p_shader->spatial.uses_alpha_scissor; + + shaders.actions_scene.usage_flag_pointers["SSS_STRENGTH"] = &p_shader->spatial.uses_sss; + shaders.actions_scene.usage_flag_pointers["DISCARD"] = &p_shader->spatial.uses_discard; + shaders.actions_scene.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->spatial.uses_screen_texture; + shaders.actions_scene.usage_flag_pointers["DEPTH_TEXTURE"] = &p_shader->spatial.uses_depth_texture; + shaders.actions_scene.usage_flag_pointers["TIME"] = &p_shader->spatial.uses_time; + + shaders.actions_scene.write_flag_pointers["MODELVIEW_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["PROJECTION_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; + shaders.actions_scene.write_flag_pointers["VERTEX"] = &p_shader->spatial.uses_vertex; + + actions = &shaders.actions_scene; + actions->uniforms = &p_shader->uniforms; + } break; + default: { return; } break; @@ -832,6 +1155,11 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const { p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; + p_shader->shader->set_custom_shader(p_shader->custom_code_id); + p_shader->shader->bind(); + + // cache uniform locations + for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { _material_make_dirty(E->self()); } @@ -919,6 +1247,10 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.type = Variant::POOL_INT_ARRAY; } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::REAL; + } break; + case ShaderLanguage::TYPE_VEC2: { pi.type = Variant::VECTOR2; } break; @@ -1071,183 +1403,1277 @@ Variant RasterizerStorageGLES2::material_get_param(RID p_material, const StringN } void RasterizerStorageGLES2::material_set_line_width(RID p_material, float p_width) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + material->line_width = p_width; } void RasterizerStorageGLES2::material_set_next_pass(RID p_material, RID p_next_material) { + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->next_pass = p_next_material; } bool RasterizerStorageGLES2::material_is_animated(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool animated = material->is_animated_cache; + if (!animated && material->next_pass.is_valid()) { + animated = material_is_animated(material->next_pass); + } + return animated; } bool RasterizerStorageGLES2::material_casts_shadows(RID p_material) { - return false; + Material *material = material_owner.get(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->dirty_list.in_list()) { + _update_material(material); + } + + bool casts_shadows = material->can_cast_shadow_cache; + + if (!casts_shadows && material->next_pass.is_valid()) { + casts_shadows = material_casts_shadows(material->next_pass); + } + + return casts_shadows; } void RasterizerStorageGLES2::material_add_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + if (E) { + E->get()++; + } else { + material->instance_owners[p_instance] = 1; + } } void RasterizerStorageGLES2::material_remove_instance_owner(RID p_material, RasterizerScene::InstanceBase *p_instance) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<RasterizerScene::InstanceBase *, int>::Element *E = material->instance_owners.find(p_instance); + ERR_FAIL_COND(!E); + + E->get()--; + + if (E->get() == 0) { + material->instance_owners.erase(E); + } } void RasterizerStorageGLES2::material_set_render_priority(RID p_material, int priority) { + ERR_FAIL_COND(priority < VS::MATERIAL_RENDER_PRIORITY_MIN); + ERR_FAIL_COND(priority > VS::MATERIAL_RENDER_PRIORITY_MAX); + + Material *material = material_owner.get(p_material); + ERR_FAIL_COND(!material); + + material->render_priority = priority; +} + +void RasterizerStorageGLES2::_update_material(Material *p_material) { + if (p_material->dirty_list.in_list()) { + _material_dirty_list.remove(&p_material->dirty_list); + } + + if (p_material->shader && p_material->shader->dirty_list.in_list()) { + _update_shader(p_material->shader); + } + + if (p_material->shader && !p_material->shader->valid) { + return; + } + + { + bool can_cast_shadow = false; + bool is_animated = false; + + if (p_material->shader && p_material->shader->mode == VS::SHADER_SPATIAL) { + + if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && + (!p_material->shader->spatial.uses_alpha || (p_material->shader->spatial.uses_alpha && p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS))) { + can_cast_shadow = true; + } + + if (p_material->shader->spatial.uses_discard && p_material->shader->uses_fragment_time) { + is_animated = true; + } + + if (p_material->shader->spatial.uses_vertex && p_material->shader->uses_vertex_time) { + is_animated = true; + } + + if (can_cast_shadow != p_material->can_cast_shadow_cache || is_animated != p_material->is_animated_cache) { + p_material->can_cast_shadow_cache = can_cast_shadow; + p_material->is_animated_cache = is_animated; + + for (Map<Geometry *, int>::Element *E = p_material->geometry_owners.front(); E; E = E->next()) { + E->key()->material_changed_notify(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) { + E->key()->base_material_changed(); + } + } + } + } + + // uniforms and other thigns will be set in the use_material method in ShaderGLES2 + + if (p_material->shader && p_material->shader->texture_count > 0) { + + p_material->textures.resize(p_material->shader->texture_count); + + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { + if (E->get().texture_order < 0) + continue; // not a texture, does not go here + + RID texture; + + Map<StringName, Variant>::Element *V = p_material->params.find(E->key()); + + if (V) { + texture = V->get(); + } + + if (!texture.is_valid()) { + Map<StringName, RID>::Element *W = p_material->shader->default_textures.find(E->key()); + + if (W) { + texture = W->get(); + } + } + + p_material->textures.write[E->get().texture_order] = Pair<StringName, RID>(E->key(), texture); + } + } else { + p_material->textures.clear(); + } +} + +void RasterizerStorageGLES2::_material_add_geometry(RID p_material, Geometry *p_geometry) { + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + + if (I) { + I->get()++; + } else { + material->geometry_owners[p_geometry] = 1; + } +} + +void RasterizerStorageGLES2::_material_remove_geometry(RID p_material, Geometry *p_geometry) { + + Material *material = material_owner.getornull(p_material); + ERR_FAIL_COND(!material); + + Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); + ERR_FAIL_COND(!I); + + I->get()--; + + if (I->get() == 0) { + material->geometry_owners.erase(I); + } } void RasterizerStorageGLES2::update_dirty_materials() { + while (_material_dirty_list.first()) { + + Material *material = _material_dirty_list.first()->self(); + _update_material(material); + } } /* MESH API */ RID RasterizerStorageGLES2::mesh_create() { - return RID(); + + Mesh *mesh = memnew(Mesh); + + return mesh_owner.make_rid(mesh); } void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS::PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes, const Vector<AABB> &p_bone_aabbs) { + PoolVector<uint8_t> array = p_array; + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(!(p_format & VS::ARRAY_FORMAT_VERTEX)); + + //must have index and bones, both. + { + uint32_t bones_weight = VS::ARRAY_FORMAT_BONES | VS::ARRAY_FORMAT_WEIGHTS; + ERR_EXPLAIN("Array must have both bones and weights in format or none."); + ERR_FAIL_COND((p_format & bones_weight) && (p_format & bones_weight) != bones_weight); + } + + //bool has_morph = p_blend_shapes.size(); + + Surface::Attrib attribs[VS::ARRAY_MAX]; + + int stride = 0; + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + + attribs[i].index = i; + + if (!(p_format & (1 << i))) { + attribs[i].enabled = false; + attribs[i].integer = false; + continue; + } + + attribs[i].enabled = true; + attribs[i].offset = stride; + attribs[i].integer = false; + + switch (i) { + + case VS::ARRAY_VERTEX: { + + if (p_format & VS::ARRAY_FLAG_USE_2D_VERTICES) { + attribs[i].size = 2; + } else { + attribs[i].size = (p_format & VS::ARRAY_COMPRESS_VERTEX) ? 4 : 3; + } + + if (p_format & VS::ARRAY_COMPRESS_VERTEX) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += attribs[i].size * 2; + } else { + attribs[i].type = GL_FLOAT; + stride += attribs[i].size * 4; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_NORMAL: { + + attribs[i].size = 3; + + if (p_format & VS::ARRAY_COMPRESS_NORMAL) { + attribs[i].type = GL_BYTE; + stride += 4; //pad extra byte + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 12; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TANGENT: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_TANGENT) { + attribs[i].type = GL_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_COLOR: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_COLOR) { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_TEX_UV: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_TEX_UV2: { + + attribs[i].size = 2; + + if (p_format & VS::ARRAY_COMPRESS_TEX_UV2) { + attribs[i].type = _GL_HALF_FLOAT_OES; + stride += 4; + } else { + attribs[i].type = GL_FLOAT; + stride += 8; + } + attribs[i].normalized = GL_FALSE; + + } break; + case VS::ARRAY_BONES: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_FLAG_USE_16_BIT_BONES) { + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + } else { + attribs[i].type = GL_UNSIGNED_BYTE; + stride += 4; + } + + attribs[i].normalized = GL_FALSE; + attribs[i].integer = true; + + } break; + case VS::ARRAY_WEIGHTS: { + + attribs[i].size = 4; + + if (p_format & VS::ARRAY_COMPRESS_WEIGHTS) { + + attribs[i].type = GL_UNSIGNED_SHORT; + stride += 8; + attribs[i].normalized = GL_TRUE; + } else { + attribs[i].type = GL_FLOAT; + stride += 16; + attribs[i].normalized = GL_FALSE; + } + + } break; + case VS::ARRAY_INDEX: { + + attribs[i].size = 1; + + if (p_vertex_count >= (1 << 16)) { + attribs[i].type = GL_UNSIGNED_INT; + attribs[i].stride = 4; + } else { + attribs[i].type = GL_UNSIGNED_SHORT; + attribs[i].stride = 2; + } + + attribs[i].normalized = GL_FALSE; + + } break; + } + } + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + attribs[i].stride = stride; + } + + //validate sizes + + int array_size = stride * p_vertex_count; + int index_array_size = 0; + if (array.size() != array_size && array.size() + p_vertex_count * 2 == array_size) { + //old format, convert + array = PoolVector<uint8_t>(); + + array.resize(p_array.size() + p_vertex_count * 2); + + PoolVector<uint8_t>::Write w = array.write(); + PoolVector<uint8_t>::Read r = p_array.read(); + + uint16_t *w16 = (uint16_t *)w.ptr(); + const uint16_t *r16 = (uint16_t *)r.ptr(); + + uint16_t one = Math::make_half_float(1); + + for (int i = 0; i < p_vertex_count; i++) { + + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = *r16++; + *w16++ = one; + for (int j = 0; j < (stride / 2) - 4; j++) { + *w16++ = *r16++; + } + } + } + + ERR_FAIL_COND(array.size() != array_size); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + + index_array_size = attribs[VS::ARRAY_INDEX].stride * p_index_count; + } + + ERR_FAIL_COND(p_index_array.size() != index_array_size); + + ERR_FAIL_COND(p_blend_shapes.size() != mesh->blend_shape_count); + + for (int i = 0; i < p_blend_shapes.size(); i++) { + ERR_FAIL_COND(p_blend_shapes[i].size() != array_size); + } + + // all valid, create stuff + + Surface *surface = memnew(Surface); + + surface->active = true; + surface->array_len = p_vertex_count; + surface->index_array_len = p_index_count; + surface->array_byte_size = array.size(); + surface->index_array_byte_size = p_index_array.size(); + surface->primitive = p_primitive; + surface->mesh = mesh; + surface->format = p_format; + surface->skeleton_bone_aabb = p_bone_aabbs; + surface->skeleton_bone_used.resize(surface->skeleton_bone_aabb.size()); + + surface->aabb = p_aabb; + surface->max_bone = p_bone_aabbs.size(); + + surface->data = array; + surface->index_data = p_index_array; + + surface->total_data_size += surface->array_byte_size + surface->index_array_byte_size; + + for (int i = 0; i < surface->skeleton_bone_used.size(); i++) { + surface->skeleton_bone_used.write[i] = surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0; + } + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + surface->attribs[i] = attribs[i]; + } + + // Okay, now the OpenGL stuff, wheeeeey \o/ + { + PoolVector<uint8_t>::Read vr = array.read(); + + glGenBuffers(1, &surface->vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), (p_format & VS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + if (p_format & VS::ARRAY_FORMAT_INDEX) { + PoolVector<uint8_t>::Read ir = p_index_array.read(); + + glGenBuffers(1, &surface->index_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_array_size, ir.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // TODO generate wireframes + } + + { + // blend shapes + + for (int i = 0; i < p_blend_shapes.size(); i++) { + + Surface::BlendShape mt; + + PoolVector<uint8_t>::Read vr = p_blend_shapes[i].read(); + + surface->total_data_size += array_size; + + glGenBuffers(1, &mt.vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, mt.vertex_id); + glBufferData(GL_ARRAY_BUFFER, array_size, vr.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + surface->blend_shapes.push_back(mt); + } + } + + mesh->surfaces.push_back(surface); + mesh->instance_change_notify(); + + info.vertex_mem += surface->total_data_size; } void RasterizerStorageGLES2::mesh_set_blend_shape_count(RID p_mesh, int p_amount) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(mesh->surfaces.size() != 0); + ERR_FAIL_COND(p_amount < 0); + + mesh->blend_shape_count = p_amount; } int RasterizerStorageGLES2::mesh_get_blend_shape_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + + return mesh->blend_shape_count; } void RasterizerStorageGLES2::mesh_set_blend_shape_mode(RID p_mesh, VS::BlendShapeMode p_mode) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->blend_shape_mode = p_mode; } VS::BlendShapeMode RasterizerStorageGLES2::mesh_get_blend_shape_mode(RID p_mesh) const { - return VS::BLEND_SHAPE_MODE_NORMALIZED; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::BLEND_SHAPE_MODE_NORMALIZED); + + return mesh->blend_shape_mode; } void RasterizerStorageGLES2::mesh_surface_update_region(RID p_mesh, int p_surface, int p_offset, const PoolVector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + int total_size = p_data.size(); + ERR_FAIL_COND(p_offset + total_size > mesh->surfaces[p_surface]->array_byte_size); + + PoolVector<uint8_t>::Read r = p_data.read(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr()); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } void RasterizerStorageGLES2::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + if (mesh->surfaces[p_surface]->material == p_material) + return; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_remove_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->surfaces[p_surface]->material = p_material; + + if (mesh->surfaces[p_surface]->material.is_valid()) { + _material_add_geometry(mesh->surfaces[p_surface]->material, mesh->surfaces[p_surface]); + } + + mesh->instance_material_change_notify(); } RID RasterizerStorageGLES2::mesh_surface_get_material(RID p_mesh, int p_surface) const { - return RID(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), RID()); + + return mesh->surfaces[p_surface]->material; } int RasterizerStorageGLES2::mesh_surface_get_array_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->array_len; } int RasterizerStorageGLES2::mesh_surface_get_array_index_len(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->index_array_len; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->data; } PoolVector<uint8_t> RasterizerStorageGLES2::mesh_surface_get_index_array(RID p_mesh, int p_surface) const { - return PoolVector<uint8_t>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, PoolVector<uint8_t>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), PoolVector<uint8_t>()); + + Surface *surface = mesh->surfaces[p_surface]; + + return surface->index_data; } uint32_t RasterizerStorageGLES2::mesh_surface_get_format(RID p_mesh, int p_surface) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + + ERR_FAIL_COND_V(!mesh, 0); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), 0); + + return mesh->surfaces[p_surface]->format; } VS::PrimitiveType RasterizerStorageGLES2::mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const { - return VS::PRIMITIVE_TRIANGLES; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, VS::PRIMITIVE_MAX); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), VS::PRIMITIVE_MAX); + + return mesh->surfaces[p_surface]->primitive; } AABB RasterizerStorageGLES2::mesh_surface_get_aabb(RID p_mesh, int p_surface) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), AABB()); + + return mesh->surfaces[p_surface]->aabb; } Vector<PoolVector<uint8_t> > RasterizerStorageGLES2::mesh_surface_get_blend_shapes(RID p_mesh, int p_surface) const { + WARN_PRINT("GLES2 mesh_surface_get_blend_shapes is not implemented"); return Vector<PoolVector<uint8_t> >(); } Vector<AABB> RasterizerStorageGLES2::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { - return Vector<AABB>(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, Vector<AABB>()); + ERR_FAIL_INDEX_V(p_surface, mesh->surfaces.size(), Vector<AABB>()); + + return mesh->surfaces[p_surface]->skeleton_bone_aabb; } void RasterizerStorageGLES2::mesh_remove_surface(RID p_mesh, int p_surface) { + + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX(p_surface, mesh->surfaces.size()); + + Surface *surface = mesh->surfaces[p_surface]; + + if (surface->material.is_valid()) { + // TODO _material_remove_geometry(surface->material, mesh->surfaces[p_surface]); + } + + glDeleteBuffers(1, &surface->vertex_id); + if (surface->index_id) { + glDeleteBuffers(1, &surface->index_id); + } + + for (int i = 0; i < surface->blend_shapes.size(); i++) { + glDeleteBuffers(1, &surface->blend_shapes[i].vertex_id); + } + + info.vertex_mem -= surface->total_data_size; + + mesh->instance_material_change_notify(); + + memdelete(surface); + + mesh->surfaces.remove(p_surface); + + mesh->instance_change_notify(); } int RasterizerStorageGLES2::mesh_get_surface_count(RID p_mesh) const { - return 0; + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + return mesh->surfaces.size(); } void RasterizerStorageGLES2::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + mesh->custom_aabb = p_aabb; } AABB RasterizerStorageGLES2::mesh_get_custom_aabb(RID p_mesh) const { - return AABB(); + const Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + return mesh->custom_aabb; } AABB RasterizerStorageGLES2::mesh_get_aabb(RID p_mesh, RID p_skeleton) const { - return AABB(); + Mesh *mesh = mesh_owner.get(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + if (mesh->custom_aabb != AABB()) + return mesh->custom_aabb; + + // TODO handle skeletons + + AABB aabb; + + if (mesh->surfaces.size() >= 1) { + aabb = mesh->surfaces[0]->aabb; + } + + for (int i = 0; i < mesh->surfaces.size(); i++) { + aabb.merge_with(mesh->surfaces[i]->aabb); + } + + return aabb; } void RasterizerStorageGLES2::mesh_clear(RID p_mesh) { + Mesh *mesh = mesh_owner.getornull(p_mesh); + ERR_FAIL_COND(!mesh); + + while (mesh->surfaces.size()) { + mesh_remove_surface(p_mesh, 0); + } } /* MULTIMESH API */ RID RasterizerStorageGLES2::multimesh_create() { - return RID(); + MultiMesh *multimesh = memnew(MultiMesh); + return multimesh_owner.make_rid(multimesh); } void RasterizerStorageGLES2::multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->size == p_instances && multimesh->transform_format == p_transform_format && multimesh->color_format == p_color_format && multimesh->custom_data_format == p_data) { + return; + } + + multimesh->size = p_instances; + + multimesh->color_format = p_color_format; + multimesh->transform_format = p_transform_format; + multimesh->custom_data_format = p_data; + + if (multimesh->size) { + multimesh->data.resize(0); + } + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->xform_floats = 8; + } else { + multimesh->xform_floats = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_NONE) { + multimesh->color_floats = 0; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + multimesh->color_floats = 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->color_floats = 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE) { + multimesh->custom_data_floats = 0; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + multimesh->custom_data_floats = 1; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->custom_data_floats = 4; + } + + int format_floats = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + + multimesh->data.resize(format_floats * p_instances); + + for (int i = 0; i < p_instances * format_floats; i += format_floats) { + int color_from = 0; + int custom_data_from = 0; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + color_from = 8; + custom_data_from = 8; + } else { + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + multimesh->data.write[i + 8] = 0.0; + multimesh->data.write[i + 9] = 0.0; + multimesh->data.write[i + 10] = 1.0; + multimesh->data.write[i + 11] = 0.0; + color_from = 12; + custom_data_from = 12; + } + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0xFFFFFFFF; + multimesh->data.write[i + color_from + 0] = cu.colf; + custom_data_from = color_from + 1; + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + multimesh->data.write[i + color_from + 0] = 1.0; + multimesh->data.write[i + color_from + 1] = 1.0; + multimesh->data.write[i + color_from + 2] = 1.0; + multimesh->data.write[i + color_from + 3] = 1.0; + custom_data_from = color_from + 4; + } + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colu = 0; + multimesh->data.write[i + custom_data_from + 0] = cu.colf; + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + multimesh->data.write[i + custom_data_from + 0] = 0.0; + multimesh->data.write[i + custom_data_from + 1] = 0.0; + multimesh->data.write[i + custom_data_from + 2] = 0.0; + multimesh->data.write[i + custom_data_from + 3] = 0.0; + } + } + + multimesh->dirty_aabb = true; + multimesh->dirty_data = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } int RasterizerStorageGLES2::multimesh_get_instance_count(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + + return multimesh->size; } void RasterizerStorageGLES2::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh->mesh = p_mesh; + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.add(&multimesh->mesh_list); + } + } + + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D); + + int stride = multimesh->color_floats + multimesh->custom_data_floats + multimesh->xform_floats; + + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.basis.elements[0][0]; + dataptr[1] = p_transform.basis.elements[0][1]; + dataptr[2] = p_transform.basis.elements[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.elements[1][0]; + dataptr[5] = p_transform.basis.elements[1][1]; + dataptr[6] = p_transform.basis.elements[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.elements[2][0]; + dataptr[9] = p_transform.basis.elements[2][1]; + dataptr[10] = p_transform.basis.elements[2][2]; + dataptr[11] = p_transform.origin.z; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + dataptr[0] = p_transform.elements[0][0]; + dataptr[1] = p_transform.elements[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.elements[2][0]; + dataptr[4] = p_transform.elements[0][1]; + dataptr[5] = p_transform.elements[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.elements[2][1]; + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_color.r * 255.0, 0, 255); + data8[1] = CLAMP(p_color.g * 255.0, 0, 255); + data8[2] = CLAMP(p_color.b * 255.0, 0, 255); + data8[3] = CLAMP(p_color.a * 255.0, 0, 255); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + dataptr[0] = p_color.r; + dataptr[1] = p_color.g; + dataptr[2] = p_color.b; + dataptr[3] = p_color.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } -void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { +void RasterizerStorageGLES2::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->size); + ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + + uint8_t *data8 = (uint8_t *)dataptr; + data8[0] = CLAMP(p_custom_data.r * 255.0, 0, 255); + data8[1] = CLAMP(p_custom_data.g * 255.0, 0, 255); + data8[2] = CLAMP(p_custom_data.b * 255.0, 0, 255); + data8[3] = CLAMP(p_custom_data.a * 255.0, 0, 255); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + dataptr[0] = p_custom_data.r; + dataptr[1] = p_custom_data.g; + dataptr[2] = p_custom_data.b; + dataptr[3] = p_custom_data.a; + } + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } RID RasterizerStorageGLES2::multimesh_get_mesh(RID p_multimesh) const { - return RID(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, RID()); + + return multimesh->mesh; } Transform RasterizerStorageGLES2::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { - return Transform(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform xform; + + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + return xform; } Transform2D RasterizerStorageGLES2::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { - return Transform2D(); + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform2D()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Transform2D()); + ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index]; + + Transform2D xform; + + xform.elements[0][0] = dataptr[0]; + xform.elements[1][0] = dataptr[1]; + xform.elements[2][0] = dataptr[3]; + xform.elements[0][1] = dataptr[4]; + xform.elements[1][1] = dataptr[5]; + xform.elements[2][1] = dataptr[7]; + + return xform; } Color RasterizerStorageGLES2::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; + + if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } Color RasterizerStorageGLES2::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->size, Color()); + ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color()); + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + + if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { + union { + uint32_t colu; + float colf; + } cu; + + cu.colf = dataptr[0]; + + return Color::hex(BSWAP32(cu.colu)); + + } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { + Color c; + c.r = dataptr[0]; + c.g = dataptr[1]; + c.b = dataptr[2]; + c.a = dataptr[3]; + + return c; + } + return Color(); } void RasterizerStorageGLES2::multimesh_set_as_bulk_array(RID p_multimesh, const PoolVector<float> &p_array) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + int dsize = multimesh->data.size(); + + ERR_FAIL_COND(dsize != p_array.size()); + + PoolVector<float>::Read r = p_array.read(); + copymem(multimesh->data.ptrw(), r.ptr(), dsize * sizeof(float)); + + multimesh->dirty_data = true; + multimesh->dirty_aabb = true; + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } } void RasterizerStorageGLES2::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND(!multimesh); + + multimesh->visible_instances = p_visible; } int RasterizerStorageGLES2::multimesh_get_visible_instances(RID p_multimesh) const { - return 0; + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, -1); + + return multimesh->visible_instances; } AABB RasterizerStorageGLES2::multimesh_get_aabb(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.getornull(p_multimesh); + ERR_FAIL_COND_V(!multimesh, AABB()); - return AABB(); + const_cast<RasterizerStorageGLES2 *>(this)->update_dirty_multimeshes(); + + return multimesh->aabb; } void RasterizerStorageGLES2::update_dirty_multimeshes() { + + while (multimesh_update_list.first()) { + + MultiMesh *multimesh = multimesh_update_list.first()->self(); + + if (multimesh->size && multimesh->dirty_aabb) { + + AABB mesh_aabb; + + if (multimesh->mesh.is_valid()) { + mesh_aabb = mesh_get_aabb(multimesh->mesh, RID()); + } else { + mesh_aabb.size += Vector3(0.001, 0.001, 0.001); + } + + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; + int count = multimesh->data.size(); + float *data = multimesh->data.ptrw(); + + AABB aabb; + + if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis[0][0] = dataptr[0]; + xform.basis[0][1] = dataptr[1]; + xform.origin[0] = dataptr[3]; + xform.basis[1][0] = dataptr[4]; + xform.basis[1][1] = dataptr[5]; + xform.origin[1] = dataptr[7]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + + } else { + + for (int i = 0; i < count; i += stride) { + + float *dataptr = &data[i]; + + Transform xform; + xform.basis.elements[0][0] = dataptr[0]; + xform.basis.elements[0][1] = dataptr[1]; + xform.basis.elements[0][2] = dataptr[2]; + xform.origin.x = dataptr[3]; + xform.basis.elements[1][0] = dataptr[4]; + xform.basis.elements[1][1] = dataptr[5]; + xform.basis.elements[1][2] = dataptr[6]; + xform.origin.y = dataptr[7]; + xform.basis.elements[2][0] = dataptr[8]; + xform.basis.elements[2][1] = dataptr[9]; + xform.basis.elements[2][2] = dataptr[10]; + xform.origin.z = dataptr[11]; + + AABB laabb = xform.xform(mesh_aabb); + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + } + + multimesh->aabb = aabb; + } + + multimesh->dirty_aabb = false; + multimesh->dirty_data = false; + + multimesh->instance_change_notify(); + + multimesh_update_list.remove(multimesh_update_list.first()); + } } /* IMMEDIATE API */ @@ -1297,119 +2723,453 @@ RID RasterizerStorageGLES2::immediate_get_material(RID p_immediate) const { /* SKELETON API */ RID RasterizerStorageGLES2::skeleton_create() { - return RID(); + + Skeleton *skeleton = memnew(Skeleton); + + return skeleton_owner.make_rid(skeleton); } void RasterizerStorageGLES2::skeleton_allocate(RID p_skeleton, int p_bones, bool p_2d_skeleton) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + ERR_FAIL_COND(p_bones < 0); + + if (skeleton->size == p_bones && skeleton->use_2d == p_2d_skeleton) { + return; + } + + skeleton->size = p_bones; + skeleton->use_2d = p_2d_skeleton; + + // TODO use float texture for vertex shader + if (config.float_texture_supported) { + glGenTextures(1, &skeleton->tex_id); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p_bones * 3, 1, 0, GL_RGB, GL_FLOAT, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + } + if (skeleton->use_2d) { + skeleton->bone_data.resize(p_bones * 4 * 2); + } else { + skeleton->bone_data.resize(p_bones * 4 * 3); + } } int RasterizerStorageGLES2::skeleton_get_bone_count(RID p_skeleton) const { - return 0; + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, 0); + + return skeleton->size; } void RasterizerStorageGLES2::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 3; + + bone_data[base_offset + 0] = p_transform.basis[0].x; + bone_data[base_offset + 1] = p_transform.basis[0].y; + bone_data[base_offset + 2] = p_transform.basis[0].z; + bone_data[base_offset + 3] = p_transform.origin.x; + + bone_data[base_offset + 4] = p_transform.basis[1].x; + bone_data[base_offset + 5] = p_transform.basis[1].y; + bone_data[base_offset + 6] = p_transform.basis[1].z; + bone_data[base_offset + 7] = p_transform.origin.y; + + bone_data[base_offset + 8] = p_transform.basis[2].x; + bone_data[base_offset + 9] = p_transform.basis[2].y; + bone_data[base_offset + 10] = p_transform.basis[2].z; + bone_data[base_offset + 11] = p_transform.origin.z; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform RasterizerStorageGLES2::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform()); + ERR_FAIL_COND_V(skeleton->use_2d, Transform()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform ret; + + int base_offset = p_bone * 4 * 3; + + ret.basis[0].x = bone_data[base_offset + 0]; + ret.basis[0].y = bone_data[base_offset + 1]; + ret.basis[0].z = bone_data[base_offset + 2]; + ret.origin.x = bone_data[base_offset + 3]; + + ret.basis[1].x = bone_data[base_offset + 4]; + ret.basis[1].y = bone_data[base_offset + 5]; + ret.basis[1].z = bone_data[base_offset + 6]; + ret.origin.y = bone_data[base_offset + 7]; + + ret.basis[2].x = bone_data[base_offset + 8]; + ret.basis[2].y = bone_data[base_offset + 9]; + ret.basis[2].z = bone_data[base_offset + 10]; + ret.origin.z = bone_data[base_offset + 11]; + + return ret; } void RasterizerStorageGLES2::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + ERR_FAIL_INDEX(p_bone, skeleton->size); + ERR_FAIL_COND(!skeleton->use_2d); + + float *bone_data = skeleton->bone_data.ptrw(); + + int base_offset = p_bone * 4 * 2; + + bone_data[base_offset + 0] = p_transform[0][0]; + bone_data[base_offset + 1] = p_transform[1][0]; + bone_data[base_offset + 2] = 0; + bone_data[base_offset + 3] = p_transform[2][0]; + bone_data[base_offset + 4] = p_transform[0][1]; + bone_data[base_offset + 5] = p_transform[1][1]; + bone_data[base_offset + 6] = 0; + bone_data[base_offset + 7] = p_transform[2][1]; + + if (!skeleton->update_list.in_list()) { + skeleton_update_list.add(&skeleton->update_list); + } } Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND_V(!skeleton, Transform2D()); + + ERR_FAIL_INDEX_V(p_bone, skeleton->size, Transform2D()); + ERR_FAIL_COND_V(!skeleton->use_2d, Transform2D()); + + const float *bone_data = skeleton->bone_data.ptr(); + + Transform2D ret; + + int base_offset = p_bone * 4 * 2; + + ret[0][0] = bone_data[base_offset + 0]; + ret[1][0] = bone_data[base_offset + 1]; + ret[2][0] = bone_data[base_offset + 3]; + ret[0][1] = bone_data[base_offset + 4]; + ret[1][1] = bone_data[base_offset + 5]; + ret[2][1] = bone_data[base_offset + 7]; + + return ret; } void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { } +void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) { + + glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer); + + if (p_size > resources.skeleton_transform_buffer_size) { + // new requested buffer is bigger, so resizing the GPU buffer + + resources.skeleton_transform_buffer_size = p_size; + + glBufferData(GL_ARRAY_BUFFER, p_size * sizeof(float), p_data.read().ptr(), GL_DYNAMIC_DRAW); + } else { + glBufferSubData(GL_ARRAY_BUFFER, 0, p_size * sizeof(float), p_data.read().ptr()); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void RasterizerStorageGLES2::update_dirty_skeletons() { + + if (!config.float_texture_supported) + return; + + glActiveTexture(GL_TEXTURE0); + + while (skeleton_update_list.first()) { + Skeleton *skeleton = skeleton_update_list.first()->self(); + + if (skeleton->size) { + glBindTexture(GL_TEXTURE_2D, skeleton->tex_id); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, skeleton->size * 3, 1, GL_RGBA, GL_FLOAT, skeleton->bone_data.ptr()); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = skeleton->instances.front(); E; E = E->next()) { + E->get()->base_changed(); + } + + skeleton_update_list.remove(skeleton_update_list.first()); + } } /* Light API */ RID RasterizerStorageGLES2::light_create(VS::LightType p_type) { - return RID(); + + Light *light = memnew(Light); + + light->type = p_type; + + light->param[VS::LIGHT_PARAM_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light->param[VS::LIGHT_PARAM_SPECULAR] = 0.5; + light->param[VS::LIGHT_PARAM_RANGE] = 1.0; + light->param[VS::LIGHT_PARAM_SPOT_ANGLE] = 45; + light->param[VS::LIGHT_PARAM_CONTACT_SHADOW_SIZE] = 45; + light->param[VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; + light->param[VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; + light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 0.1; + light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE] = 0.1; + + light->color = Color(1, 1, 1, 1); + light->shadow = false; + light->negative = false; + light->cull_mask = 0xFFFFFFFF; + light->directional_shadow_mode = VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + light->omni_shadow_mode = VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + light->omni_shadow_detail = VS::LIGHT_OMNI_SHADOW_DETAIL_VERTICAL; + light->directional_blend_splits = false; + light->directional_range_mode = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + light->reverse_cull = false; + light->version = 0; + + return light_owner.make_rid(light); } void RasterizerStorageGLES2::light_set_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->color = p_color; } void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param, float p_value) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX(p_param, VS::LIGHT_PARAM_MAX); + + switch (p_param) { + case VS::LIGHT_PARAM_RANGE: + case VS::LIGHT_PARAM_SPOT_ANGLE: + case VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: + case VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: + case VS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: + case VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: + case VS::LIGHT_PARAM_SHADOW_BIAS: { + light->version++; + light->instance_change_notify(); + } break; + } + + light->param[p_param] = p_value; } void RasterizerStorageGLES2::light_set_shadow(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow = p_enabled; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_shadow_color(RID p_light, const Color &p_color) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->shadow_color = p_color; } void RasterizerStorageGLES2::light_set_projector(RID p_light, RID p_texture) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->projector = p_texture; } void RasterizerStorageGLES2::light_set_negative(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->negative = p_enable; } void RasterizerStorageGLES2::light_set_cull_mask(RID p_light, uint32_t p_mask) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->cull_mask = p_mask; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->reverse_cull = p_enabled; } void RasterizerStorageGLES2::light_omni_set_shadow_mode(RID p_light, VS::LightOmniShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } VS::LightOmniShadowMode RasterizerStorageGLES2::light_omni_get_shadow_mode(RID p_light) { - return VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_OMNI_SHADOW_CUBE); + + return light->omni_shadow_mode; } void RasterizerStorageGLES2::light_omni_set_shadow_detail(RID p_light, VS::LightOmniShadowDetail p_detail) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_detail = p_detail; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode = p_mode; + + light->version++; + light->instance_change_notify(); } void RasterizerStorageGLES2::light_directional_set_blend_splits(RID p_light, bool p_enable) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_blend_splits = p_enable; + + light->version++; + light->instance_change_notify(); } bool RasterizerStorageGLES2::light_directional_get_blend_splits(RID p_light) const { - return false; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); + return light->directional_blend_splits; } VS::LightDirectionalShadowMode RasterizerStorageGLES2::light_directional_get_shadow_mode(RID p_light) { - return VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + return light->directional_shadow_mode; } void RasterizerStorageGLES2::light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_range_mode = p_range_mode; } VS::LightDirectionalShadowDepthRangeMode RasterizerStorageGLES2::light_directional_get_shadow_depth_range_mode(RID p_light) const { - return VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE); + + return light->directional_range_mode; } VS::LightType RasterizerStorageGLES2::light_get_type(RID p_light) const { - return VS::LIGHT_DIRECTIONAL; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL); + + return light->type; } float RasterizerStorageGLES2::light_get_param(RID p_light, VS::LightParam p_param) { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0.0); + ERR_FAIL_INDEX_V(p_param, VS::LIGHT_PARAM_MAX, 0.0); - return VS::LIGHT_DIRECTIONAL; + return light->param[p_param]; } Color RasterizerStorageGLES2::light_get_color(RID p_light) { - return Color(); + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->color; } bool RasterizerStorageGLES2::light_has_shadow(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, false); - return VS::LIGHT_DIRECTIONAL; + return light->shadow; } uint64_t RasterizerStorageGLES2::light_get_version(RID p_light) const { - return 0; + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->version; } AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const { + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, AABB()); + + switch (light->type) { + + case VS::LIGHT_SPOT: { + float len = light->param[VS::LIGHT_PARAM_RANGE]; + float size = Math::tan(Math::deg2rad(light->param[VS::LIGHT_PARAM_SPOT_ANGLE])) * len; + return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); + } break; + + case VS::LIGHT_OMNI: { + float r = light->param[VS::LIGHT_PARAM_RANGE]; + return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); + } break; + + case VS::LIGHT_DIRECTIONAL: { + return AABB(); + } break; + } + + ERR_FAIL_V(AABB()); return AABB(); } @@ -1794,6 +3554,8 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) { texture_set_flags(rt->texture, texture->flags); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // copy texscreen buffers { int w = rt->width; @@ -1867,6 +3629,7 @@ RID RasterizerStorageGLES2::render_target_create() { t->data_size = 0; t->total_data_size = 0; t->ignore_mipmaps = false; + t->compressed = false; t->mipmaps = 1; t->active = true; t->tex_id = 0; @@ -1963,14 +3726,197 @@ void RasterizerStorageGLES2::canvas_light_occluder_set_polylines(RID p_occluder, } VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { + + if (mesh_owner.owns(p_rid)) { + return VS::INSTANCE_MESH; + } + if (light_owner.owns(p_rid)) { + return VS::INSTANCE_LIGHT; + } + if (multimesh_owner.owns(p_rid)) { + return VS::INSTANCE_MULTIMESH; + } + return VS::INSTANCE_NONE; } bool RasterizerStorageGLES2::free(RID p_rid) { - return false; + + if (render_target_owner.owns(p_rid)) { + + RenderTarget *rt = render_target_owner.getornull(p_rid); + _render_target_clear(rt); + + Texture *t = texture_owner.get(rt->texture); + texture_owner.free(rt->texture); + memdelete(t); + render_target_owner.free(p_rid); + memdelete(rt); + + return true; + } else if (texture_owner.owns(p_rid)) { + + Texture *t = texture_owner.get(p_rid); + // can't free a render target texture + ERR_FAIL_COND_V(t->render_target, true); + + info.texture_mem -= t->total_data_size; + texture_owner.free(p_rid); + memdelete(t); + + return true; + } else if (sky_owner.owns(p_rid)) { + + Sky *sky = sky_owner.get(p_rid); + sky_set_texture(p_rid, RID(), 256); + sky_owner.free(p_rid); + memdelete(sky); + + return true; + } else if (shader_owner.owns(p_rid)) { + + Shader *shader = shader_owner.get(p_rid); + + if (shader->shader) { + shader->shader->free_custom_shader(shader->custom_code_id); + } + + if (shader->dirty_list.in_list()) { + _shader_dirty_list.remove(&shader->dirty_list); + } + + while (shader->materials.first()) { + Material *m = shader->materials.first()->self(); + + m->shader = NULL; + _material_make_dirty(m); + + shader->materials.remove(shader->materials.first()); + } + + shader_owner.free(p_rid); + memdelete(shader); + + return true; + } else if (material_owner.owns(p_rid)) { + + Material *m = material_owner.get(p_rid); + + if (m->shader) { + m->shader->materials.remove(&m->list); + } + + for (Map<Geometry *, int>::Element *E = m->geometry_owners.front(); E; E = E->next()) { + Geometry *g = E->key(); + g->material = RID(); + } + + for (Map<RasterizerScene::InstanceBase *, int>::Element *E = m->instance_owners.front(); E; E = E->next()) { + + RasterizerScene::InstanceBase *ins = E->key(); + + if (ins->material_override == p_rid) { + ins->material_override = RID(); + } + + for (int i = 0; i < ins->materials.size(); i++) { + if (ins->materials[i] == p_rid) { + ins->materials.write[i] = RID(); + } + } + } + + material_owner.free(p_rid); + memdelete(m); + + return true; + } else if (skeleton_owner.owns(p_rid)) { + + Skeleton *s = skeleton_owner.get(p_rid); + + if (s->update_list.in_list()) { + skeleton_update_list.remove(&s->update_list); + } + + for (Set<RasterizerScene::InstanceBase *>::Element *E = s->instances.front(); E; E = E->next()) { + E->get()->skeleton = RID(); + } + + skeleton_allocate(p_rid, 0, false); + + if (s->tex_id) { + glDeleteTextures(1, &s->tex_id); + } + + skeleton_owner.free(p_rid); + memdelete(s); + + return true; + } else if (mesh_owner.owns(p_rid)) { + + Mesh *mesh = mesh_owner.get(p_rid); + + mesh->instance_remove_deps(); + mesh_clear(p_rid); + + while (mesh->multimeshes.first()) { + MultiMesh *multimesh = mesh->multimeshes.first()->self(); + multimesh->mesh = RID(); + multimesh->dirty_aabb = true; + + mesh->multimeshes.remove(mesh->multimeshes.first()); + + if (!multimesh->update_list.in_list()) { + multimesh_update_list.add(&multimesh->update_list); + } + } + + mesh_owner.free(p_rid); + memdelete(mesh); + + return true; + } else if (multimesh_owner.owns(p_rid)) { + + MultiMesh *multimesh = multimesh_owner.get(p_rid); + multimesh->instance_remove_deps(); + + if (multimesh->mesh.is_valid()) { + Mesh *mesh = mesh_owner.getornull(multimesh->mesh); + if (mesh) { + mesh->multimeshes.remove(&multimesh->mesh_list); + } + } + + multimesh_allocate(p_rid, 0, VS::MULTIMESH_TRANSFORM_3D, VS::MULTIMESH_COLOR_NONE); + + update_dirty_multimeshes(); + + multimesh_owner.free(p_rid); + memdelete(multimesh); + + return true; + } else if (light_owner.owns(p_rid)) { + + Light *light = light_owner.get(p_rid); + light->instance_remove_deps(); + + light_owner.free(p_rid); + memdelete(light); + + return true; + } else { + return false; + } } bool RasterizerStorageGLES2::has_os_feature(const String &p_feature) const { + + if (p_feature == "s3tc") + return config.s3tc_supported; + + if (p_feature == "etc") + return config.etc1_supported; + return false; } @@ -1998,26 +3944,63 @@ void RasterizerStorageGLES2::initialize() { RasterizerStorageGLES2::system_fbo = 0; { - const char *gl_extensions = (const char *)glGetString(GL_EXTENSIONS); - Vector<String> strings = String(gl_extensions).split(" ", false); - for (int i = 0; i < strings.size(); i++) { - config.extensions.insert(strings[i]); + + const GLubyte *extension_string = glGetString(GL_EXTENSIONS); + + Vector<String> extensions = String((const char *)extension_string).split(" "); + + for (int i = 0; i < extensions.size(); i++) { + config.extensions.insert(extensions[i]); } } config.shrink_textures_x2 = false; + config.float_texture_supported = config.extensions.find("GL_ARB_texture_float") != NULL || config.extensions.find("GL_OES_texture_float") != NULL; + config.s3tc_supported = config.extensions.find("GL_EXT_texture_compression_s3tc") != NULL; + config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") != NULL; frame.count = 0; - frame.prev_tick = 0; frame.delta = 0; frame.current_rt = NULL; frame.clear_request = false; // config.keep_original_textures = false; - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); shaders.copy.init(); + shaders.cubemap_filter.init(); + + { + // quad for copying stuff + + glGenBuffers(1, &resources.quadie); + glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); + { + const float qv[16] = { + -1, + -1, + 0, + 0, + -1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + -1, + 1, + 0, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } { //default textures @@ -2074,14 +4057,55 @@ void RasterizerStorageGLES2::initialize() { glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } + + // skeleton buffer + { + resources.skeleton_transform_buffer_size = 0; + glGenBuffers(1, &resources.skeleton_transform_buffer); + } + + // radical inverse vdc cache texture + // used for cubemap filtering + if (config.float_texture_supported) { + glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); + + float radical_inverse[512]; + + for (uint32_t i = 0; i < 512; i++) { + uint32_t bits = i; + + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + float value = float(bits) * 2.3283064365386963e-10; + + radical_inverse[i] = value; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_FLOAT, radical_inverse); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void RasterizerStorageGLES2::finalize() { } +void RasterizerStorageGLES2::_copy_screen() { + bind_quad_array(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_shaders(); update_dirty_materials(); + update_dirty_skeletons(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 30e13a9f65..8bc3369dbb 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -30,6 +30,7 @@ #ifndef RASTERIZERSTORAGEGLES2_H #define RASTERIZERSTORAGEGLES2_H +#include "dvector.h" #include "self_list.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" @@ -37,11 +38,11 @@ #include "shader_gles2.h" #include "shaders/copy.glsl.gen.h" +#include "shaders/cubemap_filter.glsl.gen.h" /* #include "shaders/blend_shape.glsl.gen.h" #include "shaders/canvas.glsl.gen.h" #include "shaders/copy.glsl.gen.h" -#include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/particles.glsl.gen.h" */ @@ -76,6 +77,10 @@ public: Set<String> extensions; + bool float_texture_supported; + bool s3tc_supported; + bool etc1_supported; + bool keep_original_textures; bool no_depth_prepass; @@ -89,8 +94,13 @@ public: GLuint normal_tex; GLuint aniso_tex; + GLuint radical_inverse_vdc_cache_tex; + GLuint quadie; - GLuint quadie_array; + + size_t skeleton_transform_buffer_size; + GLuint skeleton_transform_buffer; + PoolVector<float> skeleton_transform_cpu_buffer; } resources; @@ -99,6 +109,7 @@ public: ShaderCompilerGLES2 compiler; CopyShaderGLES2 copy; + CubemapFilterShaderGLES2 cubemap_filter; ShaderCompilerGLES2::IdentifierActions actions_canvas; ShaderCompilerGLES2::IdentifierActions actions_scene; @@ -139,10 +150,72 @@ public: } info; + void bind_quad_array() const; + ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////DATA/////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// + struct Instanciable : public RID_Data { + SelfList<RasterizerScene::InstanceBase>::List instance_list; + + _FORCE_INLINE_ void instance_change_notify() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_changed(); + instances = instances->next(); + } + } + + _FORCE_INLINE_ void instance_material_change_notify() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_material_changed(); + instances = instances->next(); + } + } + + _FORCE_INLINE_ void instance_remove_deps() { + SelfList<RasterizerScene::InstanceBase> *instances = instance_list.first(); + + while (instances) { + instances->self()->base_removed(); + instances = instances->next(); + } + } + + Instanciable() {} + + virtual ~Instanciable() {} + }; + + struct GeometryOwner : public Instanciable { + }; + + struct Geometry : public Instanciable { + + enum Type { + GEOMETRY_INVALID, + GEOMETRY_SURFACE, + GEOMETRY_IMMEDIATE, + GEOMETRY_MULTISURFACE + }; + + Type type; + RID material; + uint64_t last_pass; + uint32_t index; + + virtual void material_changed_notify() {} + + Geometry() { + last_pass = 0; + index = 0; + } + }; + ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////API//////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// @@ -158,9 +231,10 @@ public: String path; uint32_t flags; - int width, height; + int width, height, depth; int alloc_width, alloc_height; Image::Format format; + VS::TextureType type; GLenum target; GLenum gl_format_cache; @@ -171,6 +245,10 @@ public: int total_data_size; bool ignore_mipmaps; + bool compressed; + + bool srgb; + int mipmaps; bool active; @@ -180,10 +258,19 @@ public: RenderTarget *render_target; - Ref<Image> images[6]; + Vector<Ref<Image> > images; bool redraw_if_visible; + VisualServer::TextureDetectCallback detect_3d; + void *detect_3d_ud; + + VisualServer::TextureDetectCallback detect_srgb; + void *detect_srgb_ud; + + VisualServer::TextureDetectCallback detect_normal; + void *detect_normal_ud; + Texture() { flags = 0; width = 0; @@ -198,6 +285,8 @@ public: total_data_size = 0; ignore_mipmaps = false; + compressed = false; + active = false; tex_id = 0; @@ -236,20 +325,22 @@ public: mutable RID_Owner<Texture> texture_owner; - Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type); + Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed); virtual RID texture_create(); - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; + virtual void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VS::TextureType p_type, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); + virtual Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; virtual Image::Format texture_get_format(RID p_texture) const; + virtual VS::TextureType texture_get_type(RID p_texture) const; virtual uint32_t texture_get_texid(RID p_texture) const; virtual uint32_t texture_get_width(RID p_texture) const; virtual uint32_t texture_get_height(RID p_texture) const; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); + virtual uint32_t texture_get_depth(RID p_texture) const; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth); virtual void texture_set_path(RID p_texture, const String &p_path); virtual String texture_get_path(RID p_texture) const; @@ -272,6 +363,15 @@ public: /* SKY API */ + struct Sky : public RID_Data { + + RID panorama; + GLuint radiance; + int radiance_size; + }; + + mutable RID_Owner<Sky> sky_owner; + virtual RID sky_create(); virtual void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size); @@ -332,7 +432,6 @@ public: } canvas_item; - /* struct Spatial { enum BlendMode { @@ -369,6 +468,7 @@ public: bool uses_discard; bool uses_sss; bool uses_screen_texture; + bool uses_depth_texture; bool uses_time; bool writes_modelview_or_projection; bool uses_vertex_lighting; @@ -379,7 +479,6 @@ public: struct Particles { } particles; - */ bool uses_vertex_time; bool uses_fragment_time; @@ -419,7 +518,7 @@ public: Map<StringName, Variant> params; SelfList<Material> list; SelfList<Material> dirty_list; - Vector<RID> textures; + Vector<Pair<StringName, RID> > textures; float line_width; int render_priority; @@ -449,6 +548,11 @@ public: mutable SelfList<Material>::List _material_dirty_list; void _material_make_dirty(Material *p_material) const; + void _material_add_geometry(RID p_material, Geometry *p_geometry); + void _material_remove_geometry(RID p_material, Geometry *p_geometry); + + void _update_material(Material *p_material); + mutable RID_Owner<Material> material_owner; virtual RID material_create(); @@ -473,6 +577,109 @@ public: void update_dirty_materials(); /* MESH API */ + + struct Mesh; + + struct Surface : public Geometry { + + struct Attrib { + bool enabled; + bool integer; + GLuint index; + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + uint32_t offset; + }; + + Attrib attribs[VS::ARRAY_MAX]; + + Mesh *mesh; + uint32_t format; + + GLuint vertex_id; + GLuint index_id; + + struct BlendShape { + GLuint vertex_id; + GLuint array_id; + }; + + Vector<BlendShape> blend_shapes; + + AABB aabb; + + int array_len; + int index_array_len; + int max_bone; + + int array_byte_size; + int index_array_byte_size; + + VS::PrimitiveType primitive; + + Vector<AABB> skeleton_bone_aabb; + Vector<bool> skeleton_bone_used; + + bool active; + + PoolVector<uint8_t> data; + PoolVector<uint8_t> index_data; + + int total_data_size; + + Surface() { + array_byte_size = 0; + index_array_byte_size = 0; + + array_len = 0; + index_array_len = 0; + + mesh = NULL; + + primitive = VS::PRIMITIVE_POINTS; + + active = false; + + total_data_size = 0; + } + }; + + struct MultiMesh; + + struct Mesh : public GeometryOwner { + + bool active; + + Vector<Surface *> surfaces; + + int blend_shape_count; + VS::BlendShapeMode blend_shape_mode; + + AABB custom_aabb; + + mutable uint64_t last_pass; + + SelfList<MultiMesh>::List multimeshes; + + _FORCE_INLINE_ void update_multimeshes() { + SelfList<MultiMesh> *mm = multimeshes.first(); + + while (mm) { + mm->self()->instance_material_change_notify(); + mm = mm->next(); + } + } + + Mesh() { + blend_shape_mode = VS::BLEND_SHAPE_MODE_NORMALIZED; + blend_shape_count = 0; + } + }; + + mutable RID_Owner<Mesh> mesh_owner; + virtual RID mesh_create(); virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, VS::PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>()); @@ -512,6 +719,55 @@ public: /* MULTIMESH API */ + struct MultiMesh : public GeometryOwner { + + RID mesh; + int size; + + VS::MultimeshTransformFormat transform_format; + VS::MultimeshColorFormat color_format; + VS::MultimeshCustomDataFormat custom_data_format; + + Vector<float> data; + + AABB aabb; + + SelfList<MultiMesh> update_list; + SelfList<MultiMesh> mesh_list; + + int visible_instances; + + int xform_floats; + int color_floats; + int custom_data_floats; + + bool dirty_aabb; + bool dirty_data; + + MultiMesh() : + update_list(this), + mesh_list(this) { + dirty_aabb = true; + dirty_data = true; + + xform_floats = 0; + color_floats = 0; + custom_data_floats = 0; + + visible_instances = -1; + + size = 0; + + transform_format = VS::MULTIMESH_TRANSFORM_2D; + color_format = VS::MULTIMESH_COLOR_NONE; + custom_data_format = VS::MULTIMESH_CUSTOM_DATA_NONE; + } + }; + + mutable RID_Owner<MultiMesh> multimesh_owner; + + SelfList<MultiMesh>::List multimesh_update_list; + virtual RID multimesh_create(); virtual void multimesh_allocate(RID p_multimesh, int p_instances, VS::MultimeshTransformFormat p_transform_format, VS::MultimeshColorFormat p_color_format, VS::MultimeshCustomDataFormat p_data = VS::MULTIMESH_CUSTOM_DATA_NONE); @@ -521,7 +777,7 @@ public: virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &p_transform); virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform); virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color); - virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color); + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_custom_data); virtual RID multimesh_get_mesh(RID p_multimesh) const; @@ -557,6 +813,33 @@ public: /* SKELETON API */ + struct Skeleton : RID_Data { + + bool use_2d; + + int size; + + // TODO use float textures for storage + + Vector<float> bone_data; + + GLuint tex_id; + + SelfList<Skeleton> update_list; + Set<RasterizerScene::InstanceBase *> instances; + + Skeleton() : + update_list(this) { + tex_id = 0; + size = 0; + use_2d = false; + } + }; + + mutable RID_Owner<Skeleton> skeleton_owner; + + SelfList<Skeleton>::List skeleton_update_list; + void update_dirty_skeletons(); virtual RID skeleton_create(); @@ -568,8 +851,38 @@ public: virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); + void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size); + /* Light API */ + struct Light : Instanciable { + VS::LightType type; + float param[VS::LIGHT_PARAM_MAX]; + + Color color; + Color shadow_color; + + RID projector; + + bool shadow; + bool negative; + bool reverse_cull; + + uint32_t cull_mask; + + VS::LightOmniShadowMode omni_shadow_mode; + VS::LightOmniShadowDetail omni_shadow_detail; + + VS::LightDirectionalShadowMode directional_shadow_mode; + VS::LightDirectionalShadowDepthRangeMode directional_range_mode; + + bool directional_blend_splits; + + uint64_t version; + }; + + mutable RID_Owner<Light> light_owner; + virtual RID light_create(VS::LightType p_type); virtual void light_set_color(RID p_light, const Color &p_color); @@ -824,7 +1137,6 @@ public: int canvas_draw_commands; float time[4]; float delta; - uint64_t prev_tick; uint64_t count; } frame; @@ -832,6 +1144,8 @@ public: void initialize(); void finalize(); + void _copy_screen(); + virtual bool has_os_feature(const String &p_feature) const; virtual void update_dirty_resources(); diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index 549d91a5a0..5ac2af6e5c 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -325,10 +325,10 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener uniform_code += ";\n"; if (SL::is_sampler_type(E->get().type)) { - r_gen_code.texture_uniforms[E->get().texture_order] = _mkid(E->key()); - r_gen_code.texture_hints[E->get().texture_order] = E->get().hint; + r_gen_code.texture_uniforms.write[E->get().texture_order] = E->key(); + r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint; } else { - r_gen_code.uniforms[E->get().order] = E->key(); + r_gen_code.uniforms.write[E->get().order] = E->key(); } vertex_global += uniform_code.as_string(); @@ -507,7 +507,6 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener case SL::OP_ASSIGN_DIV: case SL::OP_ASSIGN_SHIFT_LEFT: case SL::OP_ASSIGN_SHIFT_RIGHT: - case SL::OP_ASSIGN_MOD: case SL::OP_ASSIGN_BIT_AND: case SL::OP_ASSIGN_BIT_OR: case SL::OP_ASSIGN_BIT_XOR: { @@ -518,6 +517,16 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); } break; + case SL::OP_ASSIGN_MOD: { + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true); + code += " = "; + code += "mod("; + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, true); + code += ", "; + code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ")"; + } break; + case SL::OP_BIT_INVERT: case SL::OP_NEGATE: case SL::OP_NOT: @@ -552,6 +561,45 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += "textureCube"; } + } else if (var_node->name == "textureLod") { + // emit texture call + + if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLER2D) { + code += "texture2DLod"; + } else if (op_node->arguments[1]->get_datatype() == SL::TYPE_SAMPLERCUBE) { + code += "textureCubeLod"; + } + + } else if (var_node->name == "mix") { + + switch (op_node->arguments[3]->get_datatype()) { + + case SL::TYPE_BVEC2: { + code += "select2"; + } break; + + case SL::TYPE_BVEC3: { + code += "select3"; + } break; + + case SL::TYPE_BVEC4: { + code += "select4"; + } break; + + case SL::TYPE_VEC2: + case SL::TYPE_VEC3: + case SL::TYPE_VEC4: + case SL::TYPE_FLOAT: { + + code += "mix"; + } break; + + default: { + SL::DataType type = op_node->arguments[3]->get_datatype(); + print_line(String("uhhhh invalid mix with type: ") + itos(type)); + } break; + } + } else if (p_default_actions.renames.has(var_node->name)) { code += p_default_actions.renames[var_node->name]; } else if (internal_functions.has(var_node->name)) { @@ -590,6 +638,15 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener code += _dump_node_code(op_node->arguments[2], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); } break; + case SL::OP_MOD: { + + code += "mod("; + code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ", "; + code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); + code += ")"; + } break; + default: { code += "("; code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); @@ -647,6 +704,10 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener } code += ";\n"; } else if (cf_node->flow_op == SL::FLOW_OP_DISCARD) { + if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { + *p_actions.usage_flag_pointers["DISCARD"] = true; + used_flag_pointers.insert("DISCARD"); + } code += "discard;"; } else if (cf_node->flow_op == SL::FLOW_OP_CONTINUE) { code += "continue;"; @@ -751,10 +812,10 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { /** SPATIAL SHADER **/ actions[VS::SHADER_SPATIAL].renames["WORLD_MATRIX"] = "world_transform"; - actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_inverse_matrix"; - actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_matrix"; + actions[VS::SHADER_SPATIAL].renames["INV_CAMERA_MATRIX"] = "camera_matrix"; + actions[VS::SHADER_SPATIAL].renames["CAMERA_MATRIX"] = "camera_inverse_matrix"; actions[VS::SHADER_SPATIAL].renames["PROJECTION_MATRIX"] = "projection_matrix"; - actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; + actions[VS::SHADER_SPATIAL].renames["INV_PROJECTION_MATRIX"] = "projection_inverse_matrix"; actions[VS::SHADER_SPATIAL].renames["MODELVIEW_MATRIX"] = "modelview"; actions[VS::SHADER_SPATIAL].renames["VERTEX"] = "vertex.xyz"; diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index fa9562877d..e9b58cb272 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -33,7 +33,10 @@ #include "print_string.h" #include "string_builder.h" -//#define DEBUG_OPENGL +#include "rasterizer_gles2.h" +#include "rasterizer_storage_gles2.h" + +// #define DEBUG_OPENGL // #include "shaders/copy.glsl.gen.h" @@ -54,7 +57,7 @@ ShaderGLES2 *ShaderGLES2::active = NULL; -//#define DEBUG_SHADER +// #define DEBUG_SHADER #ifdef DEBUG_SHADER @@ -83,7 +86,10 @@ void ShaderGLES2::bind_uniforms() { continue; } - const Variant &v = E->value(); + Variant v; + + v = E->value(); + _set_uniform_variant(location, v); E = E->next(); } @@ -128,6 +134,28 @@ bool ShaderGLES2::bind() { glUseProgram(version->id); + // find out uniform names and locations + + int count; + glGetProgramiv(version->id, GL_ACTIVE_UNIFORMS, &count); + version->uniform_names.resize(count); + + for (int i = 0; i < count; i++) { + GLchar uniform_name[1024]; + int len = 0; + GLint size = 0; + GLenum type; + + glGetActiveUniform(version->id, i, 1024, &len, &size, &type, uniform_name); + + uniform_name[len] = '\0'; + String name = String((const char *)uniform_name); + + version->uniform_names.write[i] = name; + } + + bind_uniforms(); + DEBUG_TEST_ERROR("use program"); active = this; @@ -228,7 +256,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { } if (!_v) - version_map[conditional_version]; + version_map[conditional_version] = Version(); Version &v = version_map[conditional_version]; @@ -288,7 +316,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { if (cc) { for (int i = 0; i < cc->custom_defines.size(); i++) { - strings.push_back(cc->custom_defines[i]); + strings.push_back(cc->custom_defines.write[i]); DEBUG_PRINT("CD #" + itos(i) + ": " + String(cc->custom_defines[i])); } } @@ -389,6 +417,10 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { strings.push_back(fragment_code3.get_data()); #ifdef DEBUG_SHADER + + if (cc) { + DEBUG_PRINT("\nFragment Code:\n\n" + String(cc->fragment_globals)); + } DEBUG_PRINT("\nFragment Code:\n\n" + String(code_string.get_data())); #endif @@ -495,14 +527,28 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() { for (int i = 0; i < texunit_pair_count; i++) { GLint loc = glGetUniformLocation(v.id, texunit_pairs[i].name); - if (loc >= 0) - glUniform1i(loc, texunit_pairs[i].index); + if (loc >= 0) { + if (texunit_pairs[i].index < 0) { + glUniform1i(loc, max_image_units + texunit_pairs[i].index); + } else { + glUniform1i(loc, texunit_pairs[i].index); + } + } } if (cc) { - v.custom_uniform_locations.resize(cc->custom_uniforms.size()); + // uniforms for (int i = 0; i < cc->custom_uniforms.size(); i++) { - v.custom_uniform_locations[i] = glGetUniformLocation(v.id, String(cc->custom_uniforms[i]).ascii().get_data()); + StringName native_uniform_name = "m_" + cc->custom_uniforms[i]; + GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data()); + v.custom_uniform_locations[cc->custom_uniforms[i]] = location; + } + + // textures + for (int i = 0; i < cc->texture_uniforms.size(); i++) { + StringName native_uniform_name = "m_" + cc->texture_uniforms[i]; + GLint location = glGetUniformLocation(v.id, ((String)native_uniform_name).ascii().get_data()); + v.custom_uniform_locations[cc->texture_uniforms[i]] = location; } } @@ -602,6 +648,8 @@ void ShaderGLES2::setup( } } } + + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); } void ShaderGLES2::finish() { @@ -660,6 +708,7 @@ void ShaderGLES2::set_custom_shader_code(uint32_t p_code_id, cc->light = p_light; cc->custom_uniforms = p_uniforms; cc->custom_defines = p_custom_defines; + cc->texture_uniforms = p_texture_uniforms; cc->version++; } @@ -675,6 +724,379 @@ void ShaderGLES2::free_custom_shader(uint32_t p_code_id) { custom_code_map.erase(p_code_id); } +void ShaderGLES2::use_material(void *p_material) { + RasterizerStorageGLES2::Material *material = (RasterizerStorageGLES2::Material *)p_material; + + if (!material) { + return; + } + + if (!material->shader) { + return; + } + + Version *v = version_map.getptr(conditional_version); + + CustomCode *cc = NULL; + if (v) { + cc = custom_code_map.getptr(v->code_version); + } + + // bind uniforms + for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { + + if (E->get().texture_order >= 0) + continue; // this is a texture, doesn't go here + + Map<StringName, Variant>::Element *V = material->params.find(E->key()); + + Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value; + + value.first = E->get().type; + value.second = E->get().default_value; + + if (V) { + value.second = Vector<ShaderLanguage::ConstantNode::Value>(); + value.second.resize(E->get().default_value.size()); + switch (E->get().type) { + case ShaderLanguage::TYPE_BOOL: { + if (value.second.size() < 1) + value.second.resize(1); + value.second.write[0].boolean = V->get(); + } break; + + case ShaderLanguage::TYPE_BVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + } break; + + case ShaderLanguage::TYPE_BVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + value.second.write[2].boolean = flags & 4; + + } break; + + case ShaderLanguage::TYPE_BVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + int flags = V->get(); + value.second.write[0].boolean = flags & 1; + value.second.write[1].boolean = flags & 2; + value.second.write[2].boolean = flags & 4; + value.second.write[3].boolean = flags & 8; + + } break; + + case ShaderLanguage::TYPE_INT: { + if (value.second.size() < 1) + value.second.resize(1); + int val = V->get(); + value.second.write[0].sint = val; + } break; + + case ShaderLanguage::TYPE_IVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + } break; + + case ShaderLanguage::TYPE_IVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_IVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].sint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UINT: { + if (value.second.size() < 1) + value.second.resize(1); + uint32_t val = V->get(); + value.second.write[0].uint = val; + } break; + + case ShaderLanguage::TYPE_UVEC2: { + if (value.second.size() < 2) + value.second.resize(2); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UVEC3: { + if (value.second.size() < 3) + value.second.resize(3); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_UVEC4: { + if (value.second.size() < 4) + value.second.resize(4); + PoolIntArray val = V->get(); + for (int i = 0; i < val.size(); i++) { + value.second.write[i].uint = val[i]; + } + + } break; + + case ShaderLanguage::TYPE_FLOAT: { + if (value.second.size() < 1) + value.second.resize(1); + value.second.write[0].real = V->get(); + + } break; + + case ShaderLanguage::TYPE_VEC2: { + if (value.second.size() < 2) + value.second.resize(2); + Vector2 val = V->get(); + value.second.write[0].real = val.x; + value.second.write[1].real = val.y; + } break; + + case ShaderLanguage::TYPE_VEC3: { + if (value.second.size() < 3) + value.second.resize(3); + Vector3 val = V->get(); + value.second.write[0].real = val.x; + value.second.write[1].real = val.y; + value.second.write[2].real = val.z; + } break; + + case ShaderLanguage::TYPE_VEC4: { + if (value.second.size() < 4) + value.second.resize(4); + if (V->get().get_type() == Variant::PLANE) { + Plane val = V->get(); + value.second.write[0].real = val.normal.x; + value.second.write[1].real = val.normal.y; + value.second.write[2].real = val.normal.z; + value.second.write[3].real = val.d; + } else { + Color val = V->get(); + value.second.write[0].real = val.r; + value.second.write[1].real = val.g; + value.second.write[2].real = val.b; + value.second.write[3].real = val.a; + } + + } break; + + case ShaderLanguage::TYPE_MAT2: { + Transform2D val = V->get(); + + if (value.second.size() < 4) { + value.second.resize(4); + } + + value.second.write[0].real = val.elements[0][0]; + value.second.write[1].real = val.elements[0][1]; + value.second.write[2].real = val.elements[1][0]; + value.second.write[3].real = val.elements[1][1]; + + } break; + + case ShaderLanguage::TYPE_MAT3: { + Basis val = V->get(); + + if (value.second.size() < 9) { + value.second.resize(9); + } + + value.second.write[0].real = val.elements[0][0]; + value.second.write[1].real = val.elements[0][1]; + value.second.write[2].real = val.elements[0][2]; + value.second.write[3].real = val.elements[1][0]; + value.second.write[4].real = val.elements[1][1]; + value.second.write[5].real = val.elements[1][2]; + value.second.write[6].real = val.elements[2][0]; + value.second.write[7].real = val.elements[2][1]; + value.second.write[8].real = val.elements[2][2]; + } break; + + case ShaderLanguage::TYPE_MAT4: { + Transform val = V->get(); + + if (value.second.size() < 16) { + value.second.resize(16); + } + + value.second.write[0].real = val.basis.elements[0][0]; + value.second.write[1].real = val.basis.elements[0][1]; + value.second.write[2].real = val.basis.elements[0][2]; + value.second.write[3].real = 0; + value.second.write[4].real = val.basis.elements[1][0]; + value.second.write[5].real = val.basis.elements[1][1]; + value.second.write[6].real = val.basis.elements[1][2]; + value.second.write[7].real = 0; + value.second.write[8].real = val.basis.elements[2][0]; + value.second.write[9].real = val.basis.elements[2][1]; + value.second.write[10].real = val.basis.elements[2][2]; + value.second.write[11].real = 0; + value.second.write[12].real = val.origin[0]; + value.second.write[13].real = val.origin[1]; + value.second.write[14].real = val.origin[2]; + value.second.write[15].real = 1; + } break; + + case ShaderLanguage::TYPE_SAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_ISAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_USAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + } break; + } + } else { + if (value.second.size() == 0) { + // No default value set... weird, let's just use zero for everything + size_t default_arg_size = 1; + bool is_float = false; + switch (E->get().type) { + case ShaderLanguage::TYPE_BOOL: + case ShaderLanguage::TYPE_INT: + case ShaderLanguage::TYPE_UINT: { + default_arg_size = 1; + } break; + + case ShaderLanguage::TYPE_FLOAT: { + default_arg_size = 1; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_UVEC2: { + default_arg_size = 2; + } break; + + case ShaderLanguage::TYPE_VEC2: { + default_arg_size = 2; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_UVEC3: { + default_arg_size = 3; + } break; + + case ShaderLanguage::TYPE_VEC3: { + default_arg_size = 3; + is_float = true; + } break; + + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC4: { + default_arg_size = 4; + } break; + + case ShaderLanguage::TYPE_VEC4: { + default_arg_size = 4; + is_float = true; + } break; + + default: { + // TODO matricies and all that stuff + default_arg_size = 1; + } break; + } + + value.second.resize(default_arg_size); + + for (int i = 0; i < default_arg_size; i++) { + if (is_float) { + value.second.write[i].real = 0.0; + } else { + value.second.write[i].uint = 0; + } + } + } + } + + // GLint location = get_uniform_location(E->key()); + + GLint location; + if (v->custom_uniform_locations.has(E->key())) { + location = v->custom_uniform_locations[E->key()]; + } else { + int idx = v->uniform_names.find(E->key()); // TODO maybe put those in a Map? + if (idx < 0) { + location = -1; + } else { + location = v->uniform_location[idx]; + } + } + + _set_uniform_value(location, value); + } + + // bind textures + int tc = material->textures.size(); + Pair<StringName, RID> *textures = material->textures.ptrw(); + + ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = material->shader->texture_hints.ptrw(); + + for (int i = 0; i < tc; i++) { + + Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value; + value.first = ShaderLanguage::TYPE_INT; + value.second.resize(1); + value.second.write[0].sint = i; + + // GLint location = get_uniform_location(textures[i].first); + + // if (location < 0) { + // location = material->shader->uniform_locations[textures[i].first]; + // } + GLint location = -1; + if (v->custom_uniform_locations.has(textures[i].first)) { + location = v->custom_uniform_locations[textures[i].first]; + } else { + location = get_uniform_location(textures[i].first); + } + + _set_uniform_value(location, value); + } +} + void ShaderGLES2::set_base_material_tex_index(int p_idx) { } diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index c3635bc201..cb515c199c 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -44,6 +44,11 @@ #include "map.h" #include "variant.h" +#include "core/pair.h" +#include "servers/visual/shader_language.h" + +class RasterizerStorageGLES2; + class ShaderGLES2 { protected: struct Enum { @@ -105,9 +110,10 @@ private: GLuint id; GLuint vert_id; GLuint frag_id; + Vector<StringName> uniform_names; GLint *uniform_location; Vector<GLint> texture_uniform_locations; - Vector<GLint> custom_uniform_locations; + Map<StringName, GLint> custom_uniform_locations; uint32_t code_version; bool ok; Version() { @@ -169,6 +175,168 @@ private: int max_image_units; + Map<uint32_t, Variant> uniform_defaults; + Map<uint32_t, CameraMatrix> uniform_cameras; + + Map<StringName, Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > > uniform_values; + +protected: + _FORCE_INLINE_ int _get_uniform(int p_which) const; + _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); + + void setup(const char **p_conditional_defines, + int p_conditional_count, + const char **p_uniform_names, + int p_uniform_count, + const AttributePair *p_attribute_pairs, + int p_attribute_count, + const TexUnitPair *p_texunit_pairs, + int p_texunit_pair_count, + const char *p_vertex_code, + const char *p_fragment_code, + int p_vertex_code_start, + int p_fragment_code_start); + + ShaderGLES2(); + +public: + enum { + CUSTOM_SHADER_DISABLED = 0 + }; + + GLint get_uniform_location(const String &p_name) const; + GLint get_uniform_location(int p_index) const; + + static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; } + bool bind(); + void unbind(); + void bind_uniforms(); + + inline GLuint get_program() const { return version ? version->id : 0; } + + void clear_caches(); + + _FORCE_INLINE_ void _set_uniform_value(GLint p_uniform, const Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > &value) { + if (p_uniform < 0) + return; + + const Vector<ShaderLanguage::ConstantNode::Value> &values = value.second; + + switch (value.first) { + case ShaderLanguage::TYPE_BOOL: { + glUniform1i(p_uniform, values[0].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC2: { + glUniform2i(p_uniform, values[0].boolean, values[1].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC3: { + glUniform3i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean); + } break; + + case ShaderLanguage::TYPE_BVEC4: { + glUniform4i(p_uniform, values[0].boolean, values[1].boolean, values[2].boolean, values[3].boolean); + } break; + + case ShaderLanguage::TYPE_INT: { + glUniform1i(p_uniform, values[0].sint); + } break; + + case ShaderLanguage::TYPE_IVEC2: { + glUniform2i(p_uniform, values[0].sint, values[1].sint); + } break; + + case ShaderLanguage::TYPE_IVEC3: { + glUniform3i(p_uniform, values[0].sint, values[1].sint, values[2].sint); + } break; + + case ShaderLanguage::TYPE_IVEC4: { + glUniform4i(p_uniform, values[0].sint, values[1].sint, values[2].sint, values[3].sint); + } break; + + case ShaderLanguage::TYPE_UINT: { + glUniform1i(p_uniform, values[0].uint); + } break; + + case ShaderLanguage::TYPE_UVEC2: { + glUniform2i(p_uniform, values[0].uint, values[1].uint); + } break; + + case ShaderLanguage::TYPE_UVEC3: { + glUniform3i(p_uniform, values[0].uint, values[1].uint, values[2].uint); + } break; + + case ShaderLanguage::TYPE_UVEC4: { + glUniform4i(p_uniform, values[0].uint, values[1].uint, values[2].uint, values[3].uint); + } break; + + case ShaderLanguage::TYPE_FLOAT: { + glUniform1f(p_uniform, values[0].real); + } break; + + case ShaderLanguage::TYPE_VEC2: { + glUniform2f(p_uniform, values[0].real, values[1].real); + } break; + + case ShaderLanguage::TYPE_VEC3: { + glUniform3f(p_uniform, values[0].real, values[1].real, values[2].real); + } break; + + case ShaderLanguage::TYPE_VEC4: { + glUniform4f(p_uniform, values[0].real, values[1].real, values[2].real, values[3].real); + } break; + + case ShaderLanguage::TYPE_MAT2: { + GLfloat mat[4]; + + for (int i = 0; i < 4; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix2fv(p_uniform, 1, GL_FALSE, mat); + } break; + + case ShaderLanguage::TYPE_MAT3: { + GLfloat mat[9]; + + for (int i = 0; i < 9; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix3fv(p_uniform, 1, GL_FALSE, mat); + + } break; + + case ShaderLanguage::TYPE_MAT4: { + GLfloat mat[16]; + + for (int i = 0; i < 16; i++) { + mat[i] = values[i].real; + } + + glUniformMatrix4fv(p_uniform, 1, GL_FALSE, mat); + + } break; + + case ShaderLanguage::TYPE_SAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_ISAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_USAMPLER2D: { + + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + } break; + } + } + _FORCE_INLINE_ void _set_uniform_variant(GLint p_uniform, const Variant &p_value) { if (p_uniform < 0) @@ -262,49 +430,13 @@ private: glUniformMatrix4fv(p_uniform, 1, false, matrix); } break; + case Variant::OBJECT: { + + } break; default: { ERR_FAIL(); } // do nothing } } - Map<uint32_t, Variant> uniform_defaults; - Map<uint32_t, CameraMatrix> uniform_cameras; - -protected: - _FORCE_INLINE_ int _get_uniform(int p_which) const; - _FORCE_INLINE_ void _set_conditional(int p_which, bool p_value); - - void setup(const char **p_conditional_defines, - int p_conditional_count, - const char **p_uniform_names, - int p_uniform_count, - const AttributePair *p_attribute_pairs, - int p_attribute_count, - const TexUnitPair *p_texunit_pairs, - int p_texunit_pair_count, - const char *p_vertex_code, - const char *p_fragment_code, - int p_vertex_code_start, - int p_fragment_code_start); - - ShaderGLES2(); - -public: - enum { - CUSTOM_SHADER_DISABLED = 0 - }; - - GLint get_uniform_location(const String &p_name) const; - GLint get_uniform_location(int p_index) const; - - static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; } - bool bind(); - void unbind(); - void bind_uniforms(); - - inline GLuint get_program() const { return version ? version->id : 0; } - - void clear_caches(); - uint32_t create_custom_shader(); void set_custom_shader_code(uint32_t p_code_id, const String &p_vertex, @@ -331,6 +463,10 @@ public: uniforms_dirty = true; } + // this void* is actually a RasterizerStorageGLES2::Material, but C++ doesn't + // like forward declared nested classes. + void use_material(void *p_material); + uint32_t get_version() const { return new_conditional_version.version; } void set_uniform_camera(int p_idx, const CameraMatrix &p_mat) { diff --git a/drivers/gles2/shaders/SCsub b/drivers/gles2/shaders/SCsub index 5de3e1ac90..acb93fff8f 100644 --- a/drivers/gles2/shaders/SCsub +++ b/drivers/gles2/shaders/SCsub @@ -8,8 +8,8 @@ if 'GLES2_GLSL' in env['BUILDERS']: env.GLES2_GLSL('canvas.glsl'); # env.GLES2_GLSL('canvas_shadow.glsl'); env.GLES2_GLSL('scene.glsl'); -# env.GLES2_GLSL('cubemap_filter.glsl'); -# env.GLES2_GLSL('cube_to_dp.glsl'); + env.GLES2_GLSL('cubemap_filter.glsl'); + env.GLES2_GLSL('cube_to_dp.glsl'); # env.GLES2_GLSL('blend_shape.glsl'); # env.GLES2_GLSL('screen_space_reflection.glsl'); # env.GLES2_GLSL('effect_blur.glsl'); diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 11c6ab9b76..29d81bb2c4 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -27,7 +27,7 @@ uniform vec4 src_rect; #endif -uniform bool blit_pass; +uniform highp float time; VERTEX_SHADER_GLOBALS @@ -103,7 +103,7 @@ uniform mediump sampler2D normal_texture; // texunit:1 varying mediump vec2 uv_interp; varying mediump vec4 color_interp; -uniform bool blit_pass; +uniform highp float time; uniform vec4 final_modulate; @@ -127,6 +127,10 @@ void main() { vec4 color = color_interp; color *= texture2D(color_texture, uv_interp); + +#ifdef SCREEN_UV_USED + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#endif { FRAGMENT_SHADER_CODE diff --git a/drivers/gles2/shaders/copy.glsl b/drivers/gles2/shaders/copy.glsl index a21da68525..feaeb2152b 100644 --- a/drivers/gles2/shaders/copy.glsl +++ b/drivers/gles2/shaders/copy.glsl @@ -9,11 +9,20 @@ precision mediump int; #endif attribute highp vec4 vertex_attrib; // attrib:0 + +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +attribute vec3 cube_in; // attrib:4 +#else attribute vec2 uv_in; // attrib:4 +#endif + attribute vec2 uv2_in; // attrib:5 +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +varying vec3 cube_interp; +#else varying vec2 uv_interp; - +#endif varying vec2 uv2_interp; #ifdef USE_COPY_SECTION @@ -22,7 +31,12 @@ uniform vec4 copy_section; void main() { +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) + cube_interp = cube_in; +#else uv_interp = uv_in; +#endif + uv2_interp = uv2_in; gl_Position = vertex_attrib; @@ -34,6 +48,8 @@ void main() { [fragment] +#define M_PI 3.14159265359 + #ifdef USE_GLES_OVER_GL #define mediump #define highp @@ -42,21 +58,59 @@ precision mediump float; precision mediump int; #endif - +#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) +varying vec3 cube_interp; +#else varying vec2 uv_interp; +#endif + +#ifdef USE_CUBEMAP +uniform samplerCube source_cube; // texunit:0 +#else uniform sampler2D source; // texunit:0 +#endif varying vec2 uv2_interp; +#ifdef USE_MULTIPLIER +uniform float multiplier; +#endif + #ifdef USE_CUSTOM_ALPHA uniform float custom_alpha; #endif +#if defined(USE_PANORAMA) || defined(USE_ASYM_PANO) + +vec4 texturePanorama(sampler2D pano, vec3 normal) { + + vec2 st = vec2( + atan(normal.x, normal.z), + acos(normal.y) + ); + + if(st.x < 0.0) + st.x += M_PI*2.0; + + st/=vec2(M_PI*2.0,M_PI); + + return texture2D(pano,st); + +} + +#endif void main() { - //vec4 color = color_interp; +#ifdef USE_PANORAMA + + vec4 color = texturePanorama(source, normalize(cube_interp)); + +#elif defined(USE_CUBEMAP) + vec4 color = textureCube(source_cube, normalize(cube_interp)); +#else vec4 color = texture2D( source, uv_interp ); +#endif #ifdef USE_NO_ALPHA @@ -67,6 +121,9 @@ void main() { color.a=custom_alpha; #endif +#ifdef USE_MULTIPLIER + color.rgb *= multiplier; +#endif gl_FragColor = color; } diff --git a/drivers/gles2/shaders/cube_to_dp.glsl b/drivers/gles2/shaders/cube_to_dp.glsl index 5ffc78c0b9..0b3f53a870 100644 --- a/drivers/gles2/shaders/cube_to_dp.glsl +++ b/drivers/gles2/shaders/cube_to_dp.glsl @@ -1,10 +1,17 @@ [vertex] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -layout(location=0) in highp vec4 vertex_attrib; -layout(location=4) in vec2 uv_in; +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec2 uv_in; // attrib:4 -out vec2 uv_interp; +varying vec2 uv_interp; void main() { @@ -14,9 +21,16 @@ void main() { [fragment] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif uniform highp samplerCube source_cube; //texunit:0 -in vec2 uv_interp; +varying vec2 uv_interp; uniform bool z_flip; uniform highp float z_far; @@ -49,7 +63,7 @@ void main() { } //normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 )); - float depth = texture(source_cube,normal).r; + float depth = textureCube(source_cube,normal).r; // absolute values for direction cosines, bigger value equals closer to basis axis vec3 unorm = abs(normal); diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index 485fbb6ee0..62ecd9471b 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/drivers/gles2/shaders/cubemap_filter.glsl @@ -1,11 +1,17 @@ [vertex] +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -layout(location=0) in highp vec2 vertex; - -layout(location=4) in highp vec2 uv; +attribute highp vec2 vertex; // attrib:0 +attribute highp vec2 uv; // attrib:4 -out highp vec2 uv_interp; +varying highp vec2 uv_interp; void main() { @@ -15,174 +21,47 @@ void main() { [fragment] +#extension GL_ARB_shader_texture_lod : require -precision highp float; -precision highp int; +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif #ifdef USE_SOURCE_PANORAMA uniform sampler2D source_panorama; //texunit:0 -#endif - -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY -uniform sampler2DArray source_dual_paraboloid_array; //texunit:0 -uniform int source_array_index; -#endif - -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) +#else uniform samplerCube source_cube; //texunit:0 #endif uniform int face_id; uniform float roughness; -in highp vec2 uv_interp; - - -layout(location = 0) out vec4 frag_color; +varying highp vec2 uv_interp; +uniform sampler2D radical_inverse_vdc_cache; // texunit:1 #define M_PI 3.14159265359 - -vec3 texelCoordToVec(vec2 uv, int faceID) -{ - mat3 faceUvVectors[6]; -/* - // -x - faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face - - // +x - faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face - - // -y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face - - // +y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face - - // -z - faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face - - // +z - faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face -*/ - - // -x - faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face - - // +x - faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face - - // -y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face - - // +y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face - - // -z - faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face - - // +z - faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face - - // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. - vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; - return normalize(result); -} - -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) -{ - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - - // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); - float SinTheta = sqrt(1.0 - CosTheta * CosTheta); - - // Convert to spherical direction - vec3 H; - H.x = SinTheta * cos(Phi); - H.y = SinTheta * sin(Phi); - H.z = CosTheta; - - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); - - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float GGX(float NdotV, float a) -{ - float k = a / 2.0; - return NdotV / (NdotV * (1.0 - k) + k); -} - -// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float G_Smith(float a, float nDotV, float nDotL) -{ - return GGX(nDotL, a * a) * GGX(nDotV, a * a); -} - -float radicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 -} - -vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), radicalInverse_VdC(i)); -} - - - #ifdef LOW_QUALITY -#define SAMPLE_COUNT 64u +#define SAMPLE_COUNT 64 #else -#define SAMPLE_COUNT 512u +#define SAMPLE_COUNT 512 #endif -uniform bool z_flip; - #ifdef USE_SOURCE_PANORAMA -vec4 texturePanorama(vec3 normal,sampler2D pano ) { +vec4 texturePanorama(sampler2D pano, vec3 normal) { vec2 st = vec2( - atan(normal.x, normal.z), - acos(normal.y) + atan(normal.x, normal.z), + acos(normal.y) ); if(st.x < 0.0) @@ -190,105 +69,119 @@ vec4 texturePanorama(vec3 normal,sampler2D pano ) { st/=vec2(M_PI*2.0,M_PI); - return textureLod(pano,st,0.0); + return texture2DLod(pano,st,0.0); } #endif -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY - - -vec4 textureDualParaboloidArray(vec3 normal) { - - vec3 norm = normalize(normal); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z<0.0) { - norm.y=0.5-norm.y+0.5; - } - return textureLod(source_dual_paraboloid_array, vec3(norm.xy, float(source_array_index) ), 0.0); - +vec3 texelCoordToVec(vec2 uv, int faceID) { + mat3 faceUvVectors[6]; + + // -x + faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face + + // +x + faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face + + // -y + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face + + // +y + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face + + // -z + faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face + + // +z + faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face + + // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; + return normalize(result); } -#endif - -void main() { - -#ifdef USE_DUAL_PARABOLOID - - vec3 N = vec3( uv_interp * 2.0 - 1.0, 0.0 ); - N.z = 0.5 - 0.5*((N.x * N.x) + (N.y * N.y)); - N = normalize(N); - - if (z_flip) { - N.y=-N.y; //y is flipped to improve blending between both sides - N.z=-N.z; - } - +vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { + float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] -#else - vec2 uv = (uv_interp * 2.0) - 1.0; - vec3 N = texelCoordToVec(uv, face_id); -#endif - //vec4 color = color_interp; + // Compute distribution direction + float Phi = 2.0 * M_PI * Xi.x; + float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); + float SinTheta = sqrt(1.0 - CosTheta * CosTheta); -#ifdef USE_DIRECT_WRITE + // Convert to spherical direction + vec3 H; + H.x = SinTheta * cos(Phi); + H.y = SinTheta * sin(Phi); + H.z = CosTheta; -#ifdef USE_SOURCE_PANORAMA + vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 TangentX = normalize(cross(UpVector, N)); + vec3 TangentY = cross(N, TangentX); - frag_color=vec4(texturePanorama(N,source_panorama).rgb,1.0); -#endif + // Tangent to world space + return TangentX * H.x + TangentY * H.y + N * H.z; +} -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY +float radical_inverse_VdC(int i) { + return texture2D(radical_inverse_vdc_cache, vec2(float(i) / 512.0, 0.0)).x; +} - frag_color=vec4(textureDualParaboloidArray(N).rgb,1.0); -#endif +vec2 Hammersley(int i, int N) { + return vec2(float(i) / float(N), radical_inverse_VdC(i)); +} -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) +uniform bool z_flip; - N.y=-N.y; - frag_color=vec4(texture(N,source_cube).rgb,1.0); -#endif +void main() { + vec3 color = vec3(0.0); + vec2 uv = (uv_interp * 2.0) - 1.0; + vec3 N = texelCoordToVec(uv, face_id); + vec4 sum = vec4(0.0); -#else + for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) { - vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + vec2 xi = Hammersley(sample_num, SAMPLE_COUNT); - for(uint sampleNum = 0u; sampleNum < SAMPLE_COUNT; sampleNum++) { - vec2 xi = Hammersley(sampleNum, SAMPLE_COUNT); + vec3 H = ImportanceSampleGGX(xi, roughness, N); + vec3 V = N; + vec3 L = normalize(2.0 * dot(V, H) * H - V); - vec3 H = ImportanceSampleGGX( xi, roughness, N ); - vec3 V = N; - vec3 L = normalize(2.0 * dot( V, H ) * H - V); + float NdotL = clamp(dot(N, L), 0.0, 1.0); - float ndotl = clamp(dot(N, L),0.0,1.0); + if (NdotL > 0.0) { - if (ndotl>0.0) { #ifdef USE_SOURCE_PANORAMA - sum.rgb += texturePanorama(H,source_panorama).rgb *ndotl; + sum.rgb += texturePanorama(source_panorama, H).rgb * NdotL; +#else + H.y = -H.y; + sum.rgb += textureCubeLod(source_cube, H, 0.0).rgb * NdotL; #endif -#ifdef USE_SOURCE_DUAL_PARABOLOID_ARRAY - - sum.rgb += textureDualParaboloidArray(H).rgb *ndotl; -#endif + sum.a += NdotL; -#if !defined(USE_SOURCE_DUAL_PARABOLOID_ARRAY) && !defined(USE_SOURCE_PANORAMA) - H.y=-H.y; - sum.rgb += textureLod(source_cube, H, 0.0).rgb *ndotl; -#endif - sum.a += ndotl; } + } - sum /= sum.a; - frag_color = vec4(sum.rgb, 1.0); + sum /= sum.a; -#endif + gl_FragColor = vec4(sum.rgb, 1.0); } diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 79b989be4a..9251e21080 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -1,940 +1,393 @@ [vertex] -#define M_PI 3.14159265359 +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp +#else +precision mediump float; +precision mediump int; +#endif -/* -from VisualServer: +#include "stdlib.glsl" -ARRAY_VERTEX=0, -ARRAY_NORMAL=1, -ARRAY_TANGENT=2, -ARRAY_COLOR=3, -ARRAY_TEX_UV=4, -ARRAY_TEX_UV2=5, -ARRAY_BONES=6, -ARRAY_WEIGHTS=7, -ARRAY_INDEX=8, -*/ -//hack to use uv if no uv present so it works with lightmap +// +// attributes +// -/* INPUT ATTRIBS */ +attribute highp vec4 vertex_attrib; // attrib:0 +attribute vec3 normal_attrib; // attrib:1 -layout(location=0) in highp vec4 vertex_attrib; -layout(location=1) in vec3 normal_attrib; -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -layout(location=2) in vec4 tangent_attrib; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +attribute vec4 tangent_attrib; // attrib:2 #endif -#if defined(ENABLE_COLOR_INTERP) -layout(location=3) in vec4 color_attrib; +#ifdef ENABLE_COLOR_INTERP +attribute vec4 color_attrib; // attrib:3 #endif -#if defined(ENABLE_UV_INTERP) -layout(location=4) in vec2 uv_attrib; +#ifdef ENABLE_UV_INTERP +attribute vec2 uv_attrib; // attrib:4 #endif -#if defined(ENABLE_UV2_INTERP) -layout(location=5) in vec2 uv2_attrib; +#ifdef ENABLE_UV2_INTERP +attribute vec2 uv2_attrib; // attrib:5 #endif -uniform float normal_mult; - #ifdef USE_SKELETON -layout(location=6) in ivec4 bone_indices; // attrib:6 -layout(location=7) in vec4 bone_weights; // attrib:7 -#endif - -#ifdef USE_INSTANCING - -layout(location=8) in highp vec4 instance_xform0; -layout(location=9) in highp vec4 instance_xform1; -layout(location=10) in highp vec4 instance_xform2; -layout(location=11) in lowp vec4 instance_color; - -#if defined(ENABLE_INSTANCE_CUSTOM) -layout(location=12) in highp vec4 instance_custom_data; -#endif - -#endif - -layout(std140) uniform SceneData { //ubo:0 - - highp mat4 projection_matrix; - highp mat4 inv_projection_matrix; - highp mat4 camera_inverse_matrix; - highp mat4 camera_matrix; - - mediump vec4 ambient_light_color; - mediump vec4 bg_color; - - mediump vec4 fog_color_enabled; - mediump vec4 fog_sun_color_amount; - - mediump float ambient_energy; - mediump float bg_energy; - - mediump float z_offset; - mediump float z_slope_scale; - highp float shadow_dual_paraboloid_render_zfar; - highp float shadow_dual_paraboloid_render_side; - highp vec2 viewport_size; - highp vec2 screen_pixel_size; - highp vec2 shadow_atlas_pixel_size; - highp vec2 directional_shadow_pixel_size; +#ifdef USE_SKELETON_SOFTWARE - highp float time; - highp float z_far; - mediump float reflection_multiplier; - mediump float subsurface_scatter_width; - mediump float ambient_occlusion_affect_light; +attribute highp vec4 bone_transform_row_0; // attrib:9 +attribute highp vec4 bone_transform_row_1; // attrib:10 +attribute highp vec4 bone_transform_row_2; // attrib:11 - bool fog_depth_enabled; - highp float fog_depth_begin; - highp float fog_depth_curve; - bool fog_transmit_enabled; - highp float fog_transmit_curve; - bool fog_height_enabled; - highp float fog_height_min; - highp float fog_height_max; - highp float fog_height_curve; - -}; - -uniform highp mat4 world_transform; - - -#ifdef USE_LIGHT_DIRECTIONAL +#else -layout(std140) uniform DirectionalLightData { //ubo:3 +attribute vec4 bone_ids; // attrib:6 +attribute highp vec4 bone_weights; // attrib:7 - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix1; - highp mat4 shadow_matrix2; - highp mat4 shadow_matrix3; - highp mat4 shadow_matrix4; - mediump vec4 shadow_split_offsets; -}; +uniform highp sampler2D bone_transforms; // texunit:-1 +uniform ivec2 skeleton_texture_size; #endif -#ifdef USE_VERTEX_LIGHTING -//omni and spot - -struct LightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix; - -}; - - -layout(std140) uniform OmniLightData { //ubo:4 - - LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -layout(std140) uniform SpotLightData { //ubo:5 - - LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -#ifdef USE_FORWARD_LIGHTING - - -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; - -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; - #endif -out vec4 diffuse_light_interp; -out vec4 specular_light_interp; - -void light_compute(vec3 N, vec3 L,vec3 V, vec3 light_color, float roughness, inout vec3 diffuse, inout vec3 specular) { - - float dotNL = max(dot(N,L), 0.0 ); - diffuse += dotNL * light_color / M_PI; - - if (roughness > 0.0) { - - vec3 H = normalize(V + L); - float dotNH = max(dot(N,H), 0.0 ); - float intensity = pow( dotNH, (1.0-roughness) * 256.0); - specular += light_color * intensity; +#ifdef USE_INSTANCING - } -} +attribute highp vec4 instance_xform_row_0; // attrib:12 +attribute highp vec4 instance_xform_row_1; // attrib:13 +attribute highp vec4 instance_xform_row_2; // attrib:14 -void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal, float roughness,inout vec3 diffuse, inout vec3 specular) { +attribute highp vec4 instance_color; // attrib:15 +attribute highp vec4 instance_custom_data; // attrib:8 - vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w )); +#endif - light_compute(normal,normalize(light_rel_vec),eye_vec,omni_lights[idx].light_color_energy.rgb * light_attenuation,roughness,diffuse,specular); -} -void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness, inout vec3 diffuse, inout vec3 specular) { +// +// uniforms +// - vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w )); - vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz; - float spot_cutoff=spot_lights[idx].light_params.y; - float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff); - float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff); - light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x); +uniform mat4 camera_matrix; +uniform mat4 camera_inverse_matrix; +uniform mat4 projection_matrix; +uniform mat4 projection_inverse_matrix; +uniform mat4 world_transform; - light_compute(normal,normalize(light_rel_vec),eye_vec,spot_lights[idx].light_color_energy.rgb*light_attenuation,roughness,diffuse,specular); -} +uniform highp float time; +uniform float normal_mult; +#ifdef RENDER_DEPTH +uniform float light_bias; +uniform float light_normal_bias; #endif -/* Varyings */ -out highp vec3 vertex_interp; -out vec3 normal_interp; +// +// varyings +// -#if defined(ENABLE_COLOR_INTERP) -out vec4 color_interp; -#endif +varying highp vec3 vertex_interp; +varying vec3 normal_interp; -#if defined(ENABLE_UV_INTERP) -out vec2 uv_interp; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; #endif -#if defined(ENABLE_UV2_INTERP) -out vec2 uv2_interp; +#ifdef ENABLE_COLOR_INTERP +varying vec4 color_interp; #endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -out vec3 tangent_interp; -out vec3 binormal_interp; +#ifdef ENABLE_UV_INTERP +varying vec2 uv_interp; #endif - - - - -#if defined(USE_MATERIAL) - -layout(std140) uniform UniformData { //ubo:1 - -MATERIAL_UNIFORMS - -}; - -#endif - -VERTEX_SHADER_GLOBALS - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -out highp float dp_clip; - -#endif - -#define SKELETON_TEXTURE_WIDTH 256 - -#ifdef USE_SKELETON -uniform highp sampler2D skeleton_texture; //texunit:-1 +#ifdef ENABLE_UV2_INTERP +varying vec2 uv2_interp; #endif -out highp vec4 position_interp; -// FIXME: This triggers a Mesa bug that breaks rendering, so disabled for now. -// See GH-13450 and https://bugs.freedesktop.org/show_bug.cgi?id=100316 -//invariant gl_Position; +VERTEX_SHADER_GLOBALS void main() { - highp vec4 vertex = vertex_attrib; // vec4(vertex_attrib.xyz * data_attrib.x,1.0); + highp vec4 vertex = vertex_attrib; mat4 world_matrix = world_transform; - #ifdef USE_INSTANCING - { - highp mat4 m=mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0)); + highp mat4 m = mat4(instance_xform_row_0, + instance_xform_row_1, + instance_xform_row_2, + vec4(0.0, 0.0, 0.0, 1.0)); world_matrix = world_matrix * transpose(m); } #endif vec3 normal = normal_attrib * normal_mult; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) vec3 tangent = tangent_attrib.xyz; - tangent*=normal_mult; + tangent *= normal_mult; float binormalf = tangent_attrib.a; + vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#if defined(ENABLE_COLOR_INTERP) +#ifdef ENABLE_COLOR_INTERP color_interp = color_attrib; -#if defined(USE_INSTANCING) +#ifdef USE_INSTANCING color_interp *= instance_color; #endif +#endif +#ifdef ENABLE_UV_INTERP + uv_interp = uv_attrib; #endif -#ifdef USE_SKELETON - { - //skeleton transform - ivec2 tex_ofs = ivec2( bone_indices.x%256, (bone_indices.x/256)*3 ); - highp mat3x4 m = mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.x; +#ifdef ENABLE_UV2_INTERP + uv2_interp = uv2_attrib; +#endif - tex_ofs = ivec2( bone_indices.y%256, (bone_indices.y/256)*3 ); +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) + vertex = world_matrix * vertex; + normal = normalize((world_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.y; + tangent = normalize((world_matrix * vec4(tangent, 0.0)),xyz); + binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz); +#endif +#endif - tex_ofs = ivec2( bone_indices.z%256, (bone_indices.z/256)*3 ); +#ifdef USE_SKELETON - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.z; + highp mat4 bone_transform = mat4(1.0); +#ifdef USE_SKELETON_SOFTWARE + // passing the transform as attributes - tex_ofs = ivec2( bone_indices.w%256, (bone_indices.w/256)*3 ); + bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0); + bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0); + bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0); + bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0); - m+= mat3x4( - texelFetch(skeleton_texture,tex_ofs,0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0), - texelFetch(skeleton_texture,tex_ofs+ivec2(0,2),0) - ) * bone_weights.w; +#else + // look up transform from the "pose texture" + { + for (int i = 0; i < 4; i++) { + ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0); - vertex.xyz = vertex * m; + highp mat4 b = mat4(texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)), + texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)), + vec4(0.0, 0.0, 0.0, 1.0)); - normal = vec4(normal,0.0) * m; -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - tangent.xyz = vec4(tangent.xyz,0.0) * m; -#endif + bone_transform += transpose(b) * bone_weights[i]; + } } -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - vec3 binormal = normalize( cross(normal,tangent) * binormalf ); #endif -#if defined(ENABLE_UV_INTERP) - uv_interp = uv_attrib; + world_matrix = bone_transform * world_matrix; #endif -#if defined(ENABLE_UV2_INTERP) - uv2_interp = uv2_attrib; -#endif -#if defined(USE_INSTANCING) && defined(ENABLE_INSTANCE_CUSTOM) +#ifdef USE_INSTANCING vec4 instance_custom = instance_custom_data; #else vec4 instance_custom = vec4(0.0); -#endif - - highp mat4 modelview = camera_inverse_matrix * world_matrix; - highp mat4 local_projection = projection_matrix; -//using world coordinates -#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - - vertex = world_matrix * vertex; - normal = normalize((world_matrix * vec4(normal,0.0)).xyz); - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((world_matrix * vec4(tangent,0.0)).xyz); - binormal = normalize((world_matrix * vec4(binormal,0.0)).xyz); -#endif #endif - float roughness=0.0; -//defines that make writing custom shaders easier -#define projection_matrix local_projection + mat4 modelview = camera_matrix * world_matrix; + #define world_transform world_matrix + { VERTEX_SHADER_CODE } + vec4 outvec = vertex; - -//using local coordinates (default) + // use local coordinates #if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) - vertex = modelview * vertex; - normal = normalize((modelview * vec4(normal,0.0)).xyz); + normal = normalize((modelview * vec4(normal, 0.0)).xyz); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((modelview * vec4(tangent,0.0)).xyz); - binormal = normalize((modelview * vec4(binormal,0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((modelview * vec4(tangent, 0.0)).xyz); + binormal = normalize((modelview * vec4(binormal, 0.0)).xyz); #endif #endif -//using world coordinates #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - - vertex = camera_inverse_matrix * vertex; - normal = normalize((camera_inverse_matrix * vec4(normal,0.0)).xyz); - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - - tangent = normalize((camera_inverse_matrix * vec4(tangent,0.0)).xyz); - binormal = normalize((camera_inverse_matrix * vec4(binormal,0.0)).xyz); + vertex = camera_matrix * vertex; + normal = normalize((camera_matrix * vec4(normal, 0.0)).xyz); +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + tangent = normalize((camera_matrix * vec4(tangent, 0.0)).xyz); + binormal = normalize((camera_matrix * vec4(binormal, 0.0)).xyz); #endif #endif vertex_interp = vertex.xyz; normal_interp = normal; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) tangent_interp = tangent; binormal_interp = binormal; #endif - #ifdef RENDER_DEPTH + float z_ofs = light_bias; + z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias; -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - - vertex_interp.z*= shadow_dual_paraboloid_render_side; - normal_interp.z*= shadow_dual_paraboloid_render_side; - - dp_clip=vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias - - //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges - - highp vec3 vtx = vertex_interp+normalize(vertex_interp)*z_offset; - highp float distance = length(vtx); - vtx = normalize(vtx); - vtx.xy/=1.0-vtx.z; - vtx.z=(distance/shadow_dual_paraboloid_render_zfar); - vtx.z=vtx.z * 2.0 - 1.0; + vertex_interp.z -= z_ofs; - vertex.xyz=vtx; - vertex.w=1.0; - - -#else - - float z_ofs = z_offset; - z_ofs += (1.0-abs(normal_interp.z))*z_slope_scale; - vertex_interp.z-=z_ofs; - -#endif //RENDER_DEPTH_DUAL_PARABOLOID - -#endif //RENDER_DEPTH - - gl_Position = projection_matrix * vec4(vertex_interp,1.0); - - position_interp=gl_Position; - -#ifdef USE_VERTEX_LIGHTING - - diffuse_light_interp=vec4(0.0); - specular_light_interp=vec4(0.0); - -#ifdef USE_FORWARD_LIGHTING - - for(int i=0;i<omni_light_count;i++) { - light_process_omni(omni_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb); - } - - for(int i=0;i<spot_light_count;i++) { - light_process_spot(spot_light_indices[i],vertex_interp,-normalize( vertex_interp ),normal_interp,roughness,diffuse_light_interp.rgb,specular_light_interp.rgb); - } #endif -#ifdef USE_LIGHT_DIRECTIONAL - - vec3 directional_diffuse = vec3(0.0); - vec3 directional_specular = vec3(0.0); - light_compute(normal_interp,-light_direction_attenuation.xyz,-normalize( vertex_interp ),light_color_energy.rgb,roughness,directional_diffuse,directional_specular); - - float diff_avg = dot(diffuse_light_interp.rgb,vec3(0.33333)); - float diff_dir_avg = dot(directional_diffuse,vec3(0.33333)); - if (diff_avg>0.0) { - diffuse_light_interp.a=diff_dir_avg/(diff_avg+diff_dir_avg); - } else { - diffuse_light_interp.a=1.0; - } - - diffuse_light_interp.rgb+=directional_diffuse; - - float spec_avg = dot(specular_light_interp.rgb,vec3(0.33333)); - float spec_dir_avg = dot(directional_specular,vec3(0.33333)); - if (spec_avg>0.0) { - specular_light_interp.a=spec_dir_avg/(spec_avg+spec_dir_avg); - } else { - specular_light_interp.a=1.0; - } - - specular_light_interp.rgb+=directional_specular; - -#endif //USE_LIGHT_DIRECTIONAL - - -#endif // USE_VERTEX_LIGHTING + gl_Position = projection_matrix * vec4(vertex_interp, 1.0); } - [fragment] +#extension GL_ARB_shader_texture_lod : require -/* texture unit usage, N is max_texture_unity-N - -1-skeleton -2-radiance -3-reflection_atlas -4-directional_shadow -5-shadow_atlas -6-decal_atlas -7-screen -8-depth -9-probe1 -10-probe2 - -*/ - -uniform highp mat4 world_transform; - -#define M_PI 3.14159265359 - -/* Varyings */ - -#if defined(ENABLE_COLOR_INTERP) -in vec4 color_interp; -#endif - -#if defined(ENABLE_UV_INTERP) -in vec2 uv_interp; -#endif - -#if defined(ENABLE_UV2_INTERP) -in vec2 uv2_interp; -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) -in vec3 tangent_interp; -in vec3 binormal_interp; -#endif - -in highp vec3 vertex_interp; -in vec3 normal_interp; - - -/* PBR CHANNELS */ - -//used on forward mainly -uniform bool no_ambient_light; - - - -#ifdef USE_RADIANCE_MAP - - - -layout(std140) uniform Radiance { //ubo:2 - - mat4 radiance_inverse_xform; - float radiance_ambient_contribution; - -}; - -#define RADIANCE_MAX_LOD 5.0 - -#ifdef USE_RADIANCE_MAP_ARRAY - -uniform sampler2DArray radiance_map; //texunit:-2 - -vec3 textureDualParaboloid(sampler2DArray p_tex, vec3 p_vec,float p_roughness) { - - vec3 norm = normalize(p_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - - // we need to lie the derivatives (normg) and assume that DP side is always the same - // to get proper texture filtering - vec2 normg=norm.xy; - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - - // thanks to OpenGL spec using floor(layer + 0.5) for texture arrays, - // it's easy to have precision errors using fract() to interpolate layers - // as such, using fixed point to ensure it works. - - float index = p_roughness * RADIANCE_MAX_LOD; - int indexi = int(index * 256.0); - vec3 base = textureGrad(p_tex, vec3(norm.xy, float(indexi/256)),dFdx(normg),dFdy(normg)).xyz; - vec3 next = textureGrad(p_tex, vec3(norm.xy, float(indexi/256+1)),dFdx(normg),dFdy(normg)).xyz; - return mix(base,next,float(indexi%256)/256.0); -} - +#ifdef USE_GLES_OVER_GL +#define mediump +#define highp #else - -uniform sampler2D radiance_map; //texunit:-2 - -vec3 textureDualParaboloid(sampler2D p_tex, vec3 p_vec,float p_roughness) { - - vec3 norm = normalize(p_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - return textureLod(p_tex, norm.xy, p_roughness * RADIANCE_MAX_LOD).xyz; -} - +precision mediump float; +precision mediump int; #endif -#endif - -/* Material Uniforms */ - - - -#if defined(USE_MATERIAL) - -layout(std140) uniform UniformData { - -MATERIAL_UNIFORMS - -}; - -#endif - -FRAGMENT_SHADER_GLOBALS - -layout(std140) uniform SceneData { +#include "stdlib.glsl" - highp mat4 projection_matrix; - highp mat4 inv_projection_matrix; - highp mat4 camera_inverse_matrix; - highp mat4 camera_matrix; - - mediump vec4 ambient_light_color; - mediump vec4 bg_color; - - mediump vec4 fog_color_enabled; - mediump vec4 fog_sun_color_amount; - - mediump float ambient_energy; - mediump float bg_energy; - - mediump float z_offset; - mediump float z_slope_scale; - highp float shadow_dual_paraboloid_render_zfar; - highp float shadow_dual_paraboloid_render_side; - - highp vec2 viewport_size; - highp vec2 screen_pixel_size; - highp vec2 shadow_atlas_pixel_size; - highp vec2 directional_shadow_pixel_size; - - highp float time; - highp float z_far; - mediump float reflection_multiplier; - mediump float subsurface_scatter_width; - mediump float ambient_occlusion_affect_light; - - bool fog_depth_enabled; - highp float fog_depth_begin; - highp float fog_depth_curve; - bool fog_transmit_enabled; - highp float fog_transmit_curve; - bool fog_height_enabled; - highp float fog_height_min; - highp float fog_height_max; - highp float fog_height_curve; -}; - -//directional light data - -#ifdef USE_LIGHT_DIRECTIONAL - -layout(std140) uniform DirectionalLightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix1; - highp mat4 shadow_matrix2; - highp mat4 shadow_matrix3; - highp mat4 shadow_matrix4; - mediump vec4 shadow_split_offsets; -}; - - -uniform highp sampler2DShadow directional_shadow; //texunit:-4 - -#endif - -#ifdef USE_VERTEX_LIGHTING -in vec4 diffuse_light_interp; -in vec4 specular_light_interp; -#endif -//omni and spot - -struct LightData { - - highp vec4 light_pos_inv_radius; - mediump vec4 light_direction_attenuation; - mediump vec4 light_color_energy; - mediump vec4 light_params; //cone attenuation, angle, specular, shadow enabled, - mediump vec4 light_clamp; - mediump vec4 shadow_color_contact; - highp mat4 shadow_matrix; - -}; - - -layout(std140) uniform OmniLightData { //ubo:4 - - LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - -layout(std140) uniform SpotLightData { //ubo:5 - - LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; -}; - - -uniform highp sampler2DShadow shadow_atlas; //texunit:-5 - - -struct ReflectionData { - - mediump vec4 box_extents; - mediump vec4 box_offset; - mediump vec4 params; // intensity, 0, interior , boxproject - mediump vec4 ambient; //ambient color, energy - mediump vec4 atlas_clamp; - highp mat4 local_matrix; //up to here for spot and omni, rest is for directional - //notes: for ambientblend, use distance to edge to blend between already existing global environment -}; - -layout(std140) uniform ReflectionProbeData { //ubo:6 - - ReflectionData reflections[MAX_REFLECTION_DATA_STRUCTS]; -}; -uniform mediump sampler2D reflection_atlas; //texunit:-3 +#define M_PI 3.14159265359 +// +// uniforms +// -#ifdef USE_FORWARD_LIGHTING +uniform mat4 camera_matrix; +uniform mat4 camera_inverse_matrix; +uniform mat4 projection_matrix; +uniform mat4 projection_inverse_matrix; -uniform int omni_light_indices[MAX_FORWARD_LIGHTS]; -uniform int omni_light_count; +uniform mat4 world_transform; -uniform int spot_light_indices[MAX_FORWARD_LIGHTS]; -uniform int spot_light_count; +uniform highp float time; -uniform int reflection_indices[MAX_FORWARD_LIGHTS]; -uniform int reflection_count; +#ifdef SCREEN_UV_USED +uniform vec2 screen_pixel_size; #endif +uniform highp sampler2D depth_buffer; //texunit:-5 #if defined(SCREEN_TEXTURE_USED) - -uniform highp sampler2D screen_texture; //texunit:-7 - +uniform highp sampler2D screen_texture; //texunit:-6 #endif -#ifdef USE_MULTIPLE_RENDER_TARGETS +#ifdef USE_RADIANCE_MAP -layout(location=0) out vec4 diffuse_buffer; -layout(location=1) out vec4 specular_buffer; -layout(location=2) out vec4 normal_mr_buffer; -#if defined(ENABLE_SSS) -layout(location=3) out float sss_buffer; -#endif +#define RADIANCE_MAX_LOD 6.0 -#else +uniform samplerCube radiance_map; // texunit:-2 -layout(location=0) out vec4 frag_color; +uniform mat4 radiance_inverse_xform; #endif -in highp vec4 position_interp; -uniform highp sampler2D depth_buffer; //texunit:-8 - -#ifdef USE_CONTACT_SHADOWS +uniform float bg_energy; -float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) { +uniform float ambient_sky_contribution; +uniform vec4 ambient_color; +uniform float ambient_energy; - if (abs(dir.z)>0.99) - return 1.0; +#ifdef LIGHT_PASS - vec3 endpoint = pos+dir*max_distance; - vec4 source = position_interp; - vec4 dest = projection_matrix * vec4(endpoint, 1.0); +#define LIGHT_TYPE_DIRECTIONAL 0 +#define LIGHT_TYPE_OMNI 1 +#define LIGHT_TYPE_SPOT 2 - vec2 from_screen = (source.xy / source.w) * 0.5 + 0.5; - vec2 to_screen = (dest.xy / dest.w) * 0.5 + 0.5; +// general for all lights +uniform int light_type; - vec2 screen_rel = to_screen - from_screen; +uniform float light_energy; +uniform vec4 light_color; +uniform float light_specular; - if (length(screen_rel)<0.00001) - return 1.0; //too small, don't do anything +// directional +uniform vec3 light_direction; - /*float pixel_size; //approximate pixel size +// omni +uniform vec3 light_position; - if (screen_rel.x > screen_rel.y) { +uniform float light_range; +uniform vec4 light_attenuation; - pixel_size = abs((pos.x-endpoint.x)/(screen_rel.x/screen_pixel_size.x)); - } else { - pixel_size = abs((pos.y-endpoint.y)/(screen_rel.y/screen_pixel_size.y)); +// spot +uniform float light_spot_attenuation; +uniform float light_spot_range; +uniform float light_spot_angle; - }*/ - vec4 bias = projection_matrix * vec4(pos+vec3(0.0,0.0,0.04), 1.0); //todo un-harcode the 0.04 +// shadows +uniform highp sampler2D light_shadow_atlas; //texunit:-4 +uniform float light_has_shadow; +uniform mat4 light_shadow_matrix; +uniform vec4 light_clamp; - vec2 pixel_incr = normalize(screen_rel)*screen_pixel_size; +// directional shadow +uniform highp sampler2D light_directional_shadow; // texunit:-4 +uniform vec4 light_split_offsets; - float steps = length(screen_rel) / length(pixel_incr); - steps = min(2000.0,steps); //put a limit to avoid freezing in some strange situation - //steps=10.0; - - vec4 incr = (dest - source)/steps; - float ratio=0.0; - float ratio_incr = 1.0/steps; - - while(steps>0.0) { - source += incr*2.0; - bias+=incr*2.0; - - vec3 uv_depth = (source.xyz / source.w) * 0.5 + 0.5; - float depth = texture(depth_buffer,uv_depth.xy).r; - - if (depth < uv_depth.z) { - if (depth > (bias.z/bias.w) * 0.5 + 0.5) { - return min(pow(ratio,4.0),1.0); - } else { - return 1.0; - } - } - - - ratio+=ratio_incr; - steps-=1.0; - } - - return 1.0; -} - +uniform mat4 light_shadow_matrix1; +uniform mat4 light_shadow_matrix2; +uniform mat4 light_shadow_matrix3; +uniform mat4 light_shadow_matrix4; #endif -// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V. -// We're dividing this factor off because the overall term we'll end up looks like -// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012): -// -// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V) -// -// We're basically regouping this as -// -// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)] // -// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V. +// varyings // -// The contents of the D and G (G1) functions (GGX) are taken from -// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014). -// Eqns 71-72 and 85-86 (see also Eqns 43 and 80). - -float G_GGX_2cos(float cos_theta_m, float alpha) { - // Schlick's approximation - // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994) - // Eq. (19), although see Heitz (2014) the about the problems with his derivation. - // It nevertheless approximates GGX well with k = alpha/2. - float k = 0.5*alpha; - return 0.5 / (cos_theta_m * (1.0 - k) + k); - - // float cos2 = cos_theta_m*cos_theta_m; - // float sin2 = (1.0-cos2); - // return 1.0 /( cos_theta_m + sqrt(cos2 + alpha*alpha*sin2) ); -} -float D_GGX(float cos_theta_m, float alpha) { - float alpha2 = alpha*alpha; - float d = 1.0 + (alpha2-1.0)*cos_theta_m*cos_theta_m; - return alpha2/(M_PI * d * d); -} +varying highp vec3 vertex_interp; +varying vec3 normal_interp; -float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { - float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0-cos2); - float s_x = alpha_x * cos_phi; - float s_y = alpha_y * sin_phi; - return 1.0 / (cos_theta_m + sqrt(cos2 + (s_x*s_x + s_y*s_y)*sin2 )); -} +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) +varying vec3 tangent_interp; +varying vec3 binormal_interp; +#endif -float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { - float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0-cos2); - float r_x = cos_phi/alpha_x; - float r_y = sin_phi/alpha_y; - float d = cos2 + sin2*(r_x * r_x + r_y * r_y); - return 1.0 / (M_PI * alpha_x * alpha_y * d * d ); -} +#ifdef ENABLE_COLOR_INTERP +varying vec4 color_interp; +#endif +#ifdef ENABLE_UV_INTERP +varying vec2 uv_interp; +#endif -float SchlickFresnel(float u) -{ - float m = 1.0-u; - float m2 = m*m; - return m2*m2*m; // pow(m,5) -} +#ifdef ENABLE_UV2_INTERP +varying vec2 uv2_interp; +#endif -float GTR1(float NdotH, float a) -{ - if (a >= 1.0) return 1.0/M_PI; - float a2 = a*a; - float t = 1.0 + (a2-1.0)*NdotH*NdotH; - return (a2-1.0) / (M_PI*log(a2)*t); -} +varying vec3 view_interp; vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { float dielectric = (0.034 * 2.0) * specular; @@ -942,1172 +395,488 @@ vec3 metallic_to_specular_color(float metallic, float specular, vec3 albedo) { return mix(vec3(dielectric), albedo, metallic); // TODO: reference? } -void light_compute(vec3 N, vec3 L, vec3 V, vec3 B, vec3 T, vec3 light_color, vec3 attenuation, vec3 diffuse_color, vec3 transmission, float specular_blob_intensity, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, inout vec3 diffuse_light, inout vec3 specular_light) { - -#if defined(USE_LIGHT_SHADER_CODE) -//light is written by the light shader - - vec3 normal = N; - vec3 albedo = diffuse_color; - vec3 light = L; - vec3 view = V; - -LIGHT_SHADER_CODE +FRAGMENT_SHADER_GLOBALS -#else - float NdotL = dot(N,L); - float cNdotL = max(NdotL, 0.0); // clamped NdotL +#ifdef LIGHT_PASS +void light_compute(vec3 N, + vec3 L, + vec3 V, + vec3 B, + vec3 T, + vec3 light_color, + vec3 attenuation, + vec3 diffuse_color, + vec3 transmission, + float specular_blob_intensity, + float roughness, + float metallic, + float rim, + float rim_tint, + float clearcoat, + float clearcoat_gloss, + float anisotropy, + inout vec3 diffuse_light, + inout vec3 specular_light) { + + float NdotL = dot(N, L); + float cNdotL = max(NdotL, 0.0); float NdotV = dot(N, V); float cNdotV = max(NdotV, 0.0); - if (metallic < 1.0) { -#if defined(DIFFUSE_OREN_NAYAR) - vec3 diffuse_brdf_NL; -#else - float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance -#endif + { + // calculate diffuse reflection + // TODO hardcode Oren Nayar for now + float diffuse_brdf_NL; -#if defined(DIFFUSE_LAMBERT_WRAP) - //energy conserving lambert wrap shader diffuse_brdf_NL = max(0.0,(NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + // diffuse_brdf_NL = cNdotL * (1.0 / M_PI); -#elif defined(DIFFUSE_OREN_NAYAR) - - { - // see http://mimosa-pudica.net/improved-oren-nayar.html - float LdotV = dot(L, V); - - - float s = LdotV - NdotL * NdotV; - float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); - - float sigma2 = roughness * roughness; // TODO: this needs checking - vec3 A = 1.0 + sigma2 * (- 0.5 / (sigma2 + 0.33) + 0.17*diffuse_color / (sigma2 + 0.13) ); - float B = 0.45 * sigma2 / (sigma2 + 0.09); - - diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); - } - -#elif defined(DIFFUSE_TOON) - - diffuse_brdf_NL = smoothstep(-roughness,max(roughness,0.01),NdotL); - -#elif defined(DIFFUSE_BURLEY) - - { - - - vec3 H = normalize(V + L); - float cLdotH = max(0.0,dot(L, H)); - - float FD90 = 0.5 + 2.0 * cLdotH * cLdotH * roughness; - float FdV = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotV); - float FdL = 1.0 + (FD90 - 1.0) * SchlickFresnel(cNdotL); - diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL; - /* - float energyBias = mix(roughness, 0.0, 0.5); - float energyFactor = mix(roughness, 1.0, 1.0 / 1.51); - float fd90 = energyBias + 2.0 * VoH * VoH * roughness; - float f0 = 1.0; - float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0); - float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0); - - diffuse_brdf_NL = lightScatter * viewScatter * energyFactor;*/ - } -#else - //lambert - diffuse_brdf_NL = cNdotL * (1.0 / M_PI); -#endif - -#if defined(TRANSMISSION_USED) - diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation; -#else diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; -#endif - - - -#if defined(LIGHT_USE_RIM) - float rim_light = pow(1.0-cNdotV, (1.0-roughness)*16.0); - diffuse_light += rim_light * rim * mix(vec3(1.0),diffuse_color,rim_tint) * light_color; -#endif } - - if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely - - - // D - -#if defined(SPECULAR_BLINN) - - vec3 H = normalize(V + L); - float cNdotH = max(dot(N,H), 0.0 ); - float intensity = pow( cNdotH, (1.0-roughness) * 256.0); - specular_light += light_color * intensity * specular_blob_intensity * attenuation; - -#elif defined(SPECULAR_PHONG) - - vec3 R = normalize(-reflect(L,N)); - float cRdotV = max(0.0,dot(R,V)); - float intensity = pow( cRdotV, (1.0-roughness) * 256.0); - specular_light += light_color * intensity * specular_blob_intensity * attenuation; - -#elif defined(SPECULAR_TOON) + { + // calculate specular reflection vec3 R = normalize(-reflect(L,N)); - float RdotV = dot(R,V); - float mid = 1.0-roughness; - mid*=mid; - float intensity = smoothstep(mid-roughness*0.5, mid+roughness*0.5, RdotV) * mid; - diffuse_light += light_color * intensity * specular_blob_intensity * attenuation; // write to diffuse_light, as in toon shading you generally want no reflection - -#elif defined(SPECULAR_DISABLED) - //none.. - -#elif defined(SPECULAR_SCHLICK_GGX) - // shlick+ggx as default - - vec3 H = normalize(V + L); - - float cNdotH = max(dot(N,H), 0.0); - float cLdotH = max(dot(L,H), 0.0); - -# if defined(LIGHT_USE_ANISOTROPY) - - float aspect = sqrt(1.0-anisotropy*0.9); - float rx = roughness/aspect; - float ry = roughness*aspect; - float ax = rx*rx; - float ay = ry*ry; - float XdotH = dot( T, H ); - float YdotH = dot( B, H ); - float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH); - float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); - -# else - float alpha = roughness * roughness; - float D = D_GGX(cNdotH, alpha); - float G = G_GGX_2cos(cNdotL, alpha) * G_GGX_2cos(cNdotV, alpha); -# endif - // F - float F0 = 1.0; // FIXME - float cLdotH5 = SchlickFresnel(cLdotH); - float F = mix(cLdotH5, 1.0, F0); - - float specular_brdf_NL = cNdotL * D * F * G; - - specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; -#endif - -#if defined(LIGHT_USE_CLEARCOAT) - if (clearcoat_gloss > 0.0) { -# if !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_BLINN) - vec3 H = normalize(V + L); -# endif -# if !defined(SPECULAR_SCHLICK_GGX) - float cNdotH = max(dot(N,H), 0.0); - float cLdotH = max(dot(L,H), 0.0); - float cLdotH5 = SchlickFresnel(cLdotH); -#endif - float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss)); - float Fr = mix(.04, 1.0, cLdotH5); - float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25); - - - float specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; - - specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; - } -#endif - } - - -#endif //defined(USE_LIGHT_SHADER_CODE) -} - + float cRdotV = max(dot(R, V), 0.0); + float blob_intensity = pow(cRdotV, (1.0 - roughness) * 256.0); + specular_light += light_color * attenuation * blob_intensity * specular_blob_intensity; -float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 pos, float depth, vec4 clamp_rect) { - -#ifdef SHADOW_MODE_PCF_13 - - float avg=textureProj(shadow,vec4(pos,depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,-shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x*2.0,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x*2.0,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y*2.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y*2.0),depth,1.0)); - return avg*(1.0/13.0); - -#elif defined(SHADOW_MODE_PCF_5) - - float avg=textureProj(shadow,vec4(pos,depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(-shadow_pixel_size.x,0.0),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,shadow_pixel_size.y),depth,1.0)); - avg+=textureProj(shadow,vec4(pos+vec2(0.0,-shadow_pixel_size.y),depth,1.0)); - return avg*(1.0/5.0); - -#else - - return textureProj(shadow,vec4(pos,depth,1.0)); - -#endif - -} - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -in highp float dp_clip; - -#endif - - - -#if 0 -//need to save texture depth for this - -vec3 light_transmittance(float translucency,vec3 light_vec, vec3 normal, vec3 pos, float distance) { - - float scale = 8.25 * (1.0 - translucency) / subsurface_scatter_width; - float d = scale * distance; - - /** - * Armed with the thickness, we can now calculate the color by means of the - * precalculated transmittance profile. - * (It can be precomputed into a texture, for maximum performance): - */ - float dd = -d * d; - vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) + - vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) + - vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) + - vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) + - vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) + - vec3(0.078, 0.0, 0.0) * exp(dd / 7.41); - - /** - * Using the profile, we finally approximate the transmitted lighting from - * the back of the object: - */ - return profile * clamp(0.3 + dot(light_vec, normal),0.0,1.0); -} -#endif - -void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 binormal, vec3 tangent, vec3 albedo, vec3 transmission, float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss, float anisotropy, float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) { - - vec3 light_rel_vec = omni_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*omni_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w )); - - if (omni_lights[idx].light_params.w>0.5) { - //there is a shadowmap - - highp vec3 splane=(omni_lights[idx].shadow_matrix * vec4(vertex,1.0)).xyz; - float shadow_len=length(splane); - splane=normalize(splane); - vec4 clamp_rect=omni_lights[idx].light_clamp; - - if (splane.z>=0.0) { - - splane.z+=1.0; - - clamp_rect.y+=clamp_rect.w; - - } else { - - splane.z=1.0 - splane.z; - - /* - if (clamp_rect.z<clamp_rect.w) { - clamp_rect.x+=clamp_rect.z; - } else { - clamp_rect.y+=clamp_rect.w; - } - */ - - } - - splane.xy/=splane.z; - splane.xy=splane.xy * 0.5 + 0.5; - splane.z = shadow_len * omni_lights[idx].light_pos_inv_radius.w; - - splane.xy = clamp_rect.xy+splane.xy*clamp_rect.zw; - float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,clamp_rect); - -#ifdef USE_CONTACT_SHADOWS - - if (shadow>0.01 && omni_lights[idx].shadow_color_contact.a>0.0) { - - float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,omni_lights[idx].shadow_color_contact.a)); - shadow=min(shadow,contact_shadow); - - } -#endif - light_attenuation*=mix(omni_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); } - - light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,omni_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,omni_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); - } -void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 binormal, vec3 tangent,vec3 albedo, vec3 transmission,float roughness, float metallic, float rim, float rim_tint, float clearcoat, float clearcoat_gloss,float anisotropy,float p_blob_intensity, inout vec3 diffuse_light, inout vec3 specular_light) { - - vec3 light_rel_vec = spot_lights[idx].light_pos_inv_radius.xyz-vertex; - float light_length = length( light_rel_vec ); - float normalized_distance = light_length*spot_lights[idx].light_pos_inv_radius.w; - vec3 light_attenuation = vec3(pow( max(1.0 - normalized_distance, 0.001), spot_lights[idx].light_direction_attenuation.w )); - vec3 spot_dir = spot_lights[idx].light_direction_attenuation.xyz; - float spot_cutoff=spot_lights[idx].light_params.y; - float scos = max(dot(-normalize(light_rel_vec), spot_dir),spot_cutoff); - float spot_rim = (1.0 - scos) / (1.0 - spot_cutoff); - light_attenuation *= 1.0 - pow( max(spot_rim,0.001), spot_lights[idx].light_params.x); - - if (spot_lights[idx].light_params.w>0.5) { - //there is a shadowmap - highp vec4 splane=(spot_lights[idx].shadow_matrix * vec4(vertex,1.0)); - splane.xyz/=splane.w; - - float shadow = sample_shadow(shadow_atlas,shadow_atlas_pixel_size,splane.xy,splane.z,spot_lights[idx].light_clamp); - -#ifdef USE_CONTACT_SHADOWS - if (shadow>0.01 && spot_lights[idx].shadow_color_contact.a>0.0) { - - float contact_shadow = contact_shadow_compute(vertex,normalize(light_rel_vec),min(light_length,spot_lights[idx].shadow_color_contact.a)); - shadow=min(shadow,contact_shadow); - - } -#endif - light_attenuation*=mix(spot_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); - } - - light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,spot_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,spot_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); - -} - -void reflection_process(int idx, vec3 vertex, vec3 normal,vec3 binormal, vec3 tangent,float roughness,float anisotropy,vec3 ambient,vec3 skybox, inout highp vec4 reflection_accum,inout highp vec4 ambient_accum) { - - vec3 ref_vec = normalize(reflect(vertex,normal)); - vec3 local_pos = (reflections[idx].local_matrix * vec4(vertex,1.0)).xyz; - vec3 box_extents = reflections[idx].box_extents.xyz; - - if (any(greaterThan(abs(local_pos),box_extents))) { //out of the reflection box - return; - } - - vec3 inner_pos = abs(local_pos / box_extents); - float blend = max(inner_pos.x,max(inner_pos.y,inner_pos.z)); - //make blend more rounded - blend=mix(length(inner_pos),blend,blend); - blend*=blend; - blend=1.001-blend; - - if (reflections[idx].params.x>0.0){// compute reflection - - vec3 local_ref_vec = (reflections[idx].local_matrix * vec4(ref_vec,0.0)).xyz; - - if (reflections[idx].params.w > 0.5) { //box project - - vec3 nrdir = normalize(local_ref_vec); - vec3 rbmax = (box_extents - local_pos)/nrdir; - vec3 rbmin = (-box_extents - local_pos)/nrdir; - - - vec3 rbminmax = mix(rbmin,rbmax,greaterThan(nrdir,vec3(0.0,0.0,0.0))); - - float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); - vec3 posonbox = local_pos + nrdir * fa; - local_ref_vec = posonbox - reflections[idx].box_offset.xyz; - } - - - vec4 clamp_rect=reflections[idx].atlas_clamp; - vec3 norm = normalize(local_ref_vec); - norm.xy/=1.0+abs(norm.z); - norm.xy=norm.xy * vec2(0.5,0.25) + vec2(0.5,0.25); - if (norm.z>0.0) { - norm.y=0.5-norm.y+0.5; - } - - vec2 atlas_uv = norm.xy * clamp_rect.zw + clamp_rect.xy; - atlas_uv = clamp(atlas_uv,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw); - - highp vec4 reflection; - reflection.rgb = textureLod(reflection_atlas,atlas_uv,roughness*5.0).rgb; - - if (reflections[idx].params.z < 0.5) { - reflection.rgb = mix(skybox,reflection.rgb,blend); - } - reflection.rgb*=reflections[idx].params.x; - reflection.a = blend; - reflection.rgb*=reflection.a; - - reflection_accum+=reflection; - } - - if (reflections[idx].ambient.a>0.0) { //compute ambient using skybox - vec3 local_amb_vec = (reflections[idx].local_matrix * vec4(normal,0.0)).xyz; - vec3 splane=normalize(local_amb_vec); - vec4 clamp_rect=reflections[idx].atlas_clamp; +// shadows - splane.z*=-1.0; - if (splane.z>=0.0) { - splane.z+=1.0; - clamp_rect.y+=clamp_rect.w; - } else { - splane.z=1.0 - splane.z; - splane.y=-splane.y; - } - - splane.xy/=splane.z; - splane.xy=splane.xy * 0.5 + 0.5; - - splane.xy = splane.xy * clamp_rect.zw + clamp_rect.xy; - splane.xy = clamp(splane.xy,clamp_rect.xy,clamp_rect.xy+clamp_rect.zw); - - highp vec4 ambient_out; - ambient_out.a=blend; - ambient_out.rgb = textureLod(reflection_atlas,splane.xy,5.0).rgb; - ambient_out.rgb=mix(reflections[idx].ambient.rgb,ambient_out.rgb,reflections[idx].ambient.a); - if (reflections[idx].params.z < 0.5) { - ambient_out.rgb = mix(ambient,ambient_out.rgb,blend); - } - - ambient_out.rgb *= ambient_out.a; - ambient_accum+=ambient_out; - } else { - - highp vec4 ambient_out; - ambient_out.a=blend; - ambient_out.rgb=reflections[idx].ambient.rgb; - if (reflections[idx].params.z < 0.5) { - ambient_out.rgb = mix(ambient,ambient_out.rgb,blend); - } - ambient_out.rgb *= ambient_out.a; - ambient_accum+=ambient_out; - - } -} - -#ifdef USE_GI_PROBES - -uniform mediump sampler3D gi_probe1; //texunit:-9 -uniform highp mat4 gi_probe_xform1; -uniform highp vec3 gi_probe_bounds1; -uniform highp vec3 gi_probe_cell_size1; -uniform highp float gi_probe_multiplier1; -uniform highp float gi_probe_bias1; -uniform highp float gi_probe_normal_bias1; -uniform bool gi_probe_blend_ambient1; - -uniform mediump sampler3D gi_probe2; //texunit:-10 -uniform highp mat4 gi_probe_xform2; -uniform highp vec3 gi_probe_bounds2; -uniform highp vec3 gi_probe_cell_size2; -uniform highp float gi_probe_multiplier2; -uniform highp float gi_probe_bias2; -uniform highp float gi_probe_normal_bias2; -uniform bool gi_probe2_enabled; -uniform bool gi_probe_blend_ambient2; - -vec3 voxel_cone_trace(mediump sampler3D probe, vec3 cell_size, vec3 pos, vec3 ambient, bool blend_ambient, vec3 direction, float tan_half_angle, float max_distance, float p_bias) { - - float dist = p_bias;//1.0; //dot(direction,mix(vec3(-1.0),vec3(1.0),greaterThan(direction,vec3(0.0))))*2.0; - float alpha=0.0; - vec3 color = vec3(0.0); - - while(dist < max_distance && alpha < 0.95) { - float diameter = max(1.0, 2.0 * tan_half_angle * dist); - vec4 scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) ); - float a = (1.0 - alpha); - color += scolor.rgb * a; - alpha += a * scolor.a; - dist += diameter * 0.5; - } - - if (blend_ambient) { - color.rgb = mix(ambient,color.rgb,min(1.0,alpha/0.95)); - } - - return color; -} - -void gi_probe_compute(mediump sampler3D probe, mat4 probe_xform, vec3 bounds,vec3 cell_size,vec3 pos, vec3 ambient, vec3 environment, bool blend_ambient,float multiplier, mat3 normal_mtx,vec3 ref_vec, float roughness,float p_bias,float p_normal_bias, inout vec4 out_spec, inout vec4 out_diff) { - - - - vec3 probe_pos = (probe_xform * vec4(pos,1.0)).xyz; - vec3 ref_pos = (probe_xform * vec4(pos+ref_vec,1.0)).xyz; - ref_vec = normalize(ref_pos - probe_pos); - - probe_pos+=(probe_xform * vec4(normal_mtx[2],0.0)).xyz*p_normal_bias; - -/* out_diff.rgb = voxel_cone_trace(probe,cell_size,probe_pos,normalize((probe_xform * vec4(ref_vec,0.0)).xyz),0.0 ,100.0); - out_diff.a = 1.0; - return;*/ - //out_diff = vec4(textureLod(probe,probe_pos*cell_size,3.0).rgb,1.0); - //return; - - //this causes corrupted pixels, i have no idea why.. - if (any(bvec2(any(lessThan(probe_pos,vec3(0.0))),any(greaterThan(probe_pos,bounds))))) { - return; - } - - //vec3 blendv = probe_pos/bounds * 2.0 - 1.0; - //float blend = 1.001-max(blendv.x,max(blendv.y,blendv.z)); - float blend=1.0; - - float max_distance = length(bounds); - - //radiance -#ifdef VCT_QUALITY_HIGH - -#define MAX_CONE_DIRS 6 - vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( - vec3(0, 0, 1), - vec3(0.866025, 0, 0.5), - vec3(0.267617, 0.823639, 0.5), - vec3(-0.700629, 0.509037, 0.5), - vec3(-0.700629, -0.509037, 0.5), - vec3(0.267617, -0.823639, 0.5) - ); - - float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15); - float cone_angle_tan = 0.577; - float min_ref_tan = 0.0; -#else - -#define MAX_CONE_DIRS 4 - - vec3 cone_dirs[MAX_CONE_DIRS] = vec3[] ( - vec3(0.707107, 0, 0.707107), - vec3(0, 0.707107, 0.707107), - vec3(-0.707107, 0, 0.707107), - vec3(0, -0.707107, 0.707107) - ); - - float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25); - float cone_angle_tan = 0.98269; - max_distance*=0.5; - float min_ref_tan = 0.2; - -#endif - vec3 light=vec3(0.0); - for(int i=0;i<MAX_CONE_DIRS;i++) { - - vec3 dir = normalize( (probe_xform * vec4(pos + normal_mtx * cone_dirs[i],1.0)).xyz - probe_pos); - light+=cone_weights[i] * voxel_cone_trace(probe,cell_size,probe_pos,ambient,blend_ambient,dir,cone_angle_tan,max_distance,p_bias); - - } - - light*=multiplier; - - out_diff += vec4(light*blend,blend); - - //irradiance - - vec3 irr_light = voxel_cone_trace(probe,cell_size,probe_pos,environment,blend_ambient,ref_vec,max(min_ref_tan,tan(roughness * 0.5 * M_PI)) ,max_distance,p_bias); - - irr_light *= multiplier; - //irr_light=vec3(0.0); - - out_spec += vec4(irr_light*blend,blend); +float sample_shadow(highp sampler2D shadow, + vec2 shadow_pixel_size, + vec2 pos, + float depth, + vec4 clamp_rect) +{ + // vec4 depth_value = texture2D(shadow, pos); + // return depth_value.z; + return texture2DProj(shadow, vec4(pos, depth, 1.0)).r; + // return (depth_value.x + depth_value.y + depth_value.z + depth_value.w) / 4.0; } -void gi_probes_compute(vec3 pos, vec3 normal, float roughness, inout vec3 out_specular, inout vec3 out_ambient) { - - roughness = roughness * roughness; - - vec3 ref_vec = normalize(reflect(normalize(pos),normal)); - - //find arbitrary tangent and bitangent, then build a matrix - vec3 v0 = abs(normal.z) < 0.999 ? vec3(0, 0, 1) : vec3(0, 1, 0); - vec3 tangent = normalize(cross(v0, normal)); - vec3 bitangent = normalize(cross(tangent, normal)); - mat3 normal_mat = mat3(tangent,bitangent,normal); - - vec4 diff_accum = vec4(0.0); - vec4 spec_accum = vec4(0.0); - - vec3 ambient = out_ambient; - out_ambient = vec3(0.0); - - vec3 environment = out_specular; - - out_specular = vec3(0.0); - - gi_probe_compute(gi_probe1,gi_probe_xform1,gi_probe_bounds1,gi_probe_cell_size1,pos,ambient,environment,gi_probe_blend_ambient1,gi_probe_multiplier1,normal_mat,ref_vec,roughness,gi_probe_bias1,gi_probe_normal_bias1,spec_accum,diff_accum); - - if (gi_probe2_enabled) { - - gi_probe_compute(gi_probe2,gi_probe_xform2,gi_probe_bounds2,gi_probe_cell_size2,pos,ambient,environment,gi_probe_blend_ambient2,gi_probe_multiplier2,normal_mat,ref_vec,roughness,gi_probe_bias2,gi_probe_normal_bias2,spec_accum,diff_accum); - } - - if (diff_accum.a>0.0) { - diff_accum.rgb/=diff_accum.a; - } - - if (spec_accum.a>0.0) { - spec_accum.rgb/=spec_accum.a; - } - - out_specular+=spec_accum.rgb; - out_ambient+=diff_accum.rgb; - -} - #endif +void main() +{ - -void main() { - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - - if (dp_clip>0.0) - discard; -#endif - - //lay out everything, whathever is unused is optimized away anyway highp vec3 vertex = vertex_interp; - vec3 albedo = vec3(0.8,0.8,0.8); + vec3 albedo = vec3(1.0); vec3 transmission = vec3(0.0); float metallic = 0.0; float specular = 0.5; - vec3 emission = vec3(0.0,0.0,0.0); + vec3 emission = vec3(0.0); float roughness = 1.0; float rim = 0.0; float rim_tint = 0.0; - float clearcoat=0.0; - float clearcoat_gloss=0.0; - float anisotropy = 1.0; - vec2 anisotropy_flow = vec2(1.0,0.0); - -#if defined(ENABLE_AO) - float ao=1.0; - float ao_light_affect=0.0; -#endif + float clearcoat = 0.0; + float clearcoat_gloss = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); float alpha = 1.0; + float side = 1.0; -#ifdef METERIAL_DOUBLESIDED - float side=float(gl_FrontFacing)*2.0-1.0; -#else - float side=1.0; +#if defined(ENABLE_AO) + float ao = 1.0; + float ao_light_affect = 0.0; #endif -#if defined(ALPHA_SCISSOR_USED) - float alpha_scissor = 0.5; -#endif - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY) - vec3 binormal = normalize(binormal_interp)*side; - vec3 tangent = normalize(tangent_interp)*side; +#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) + vec3 binormal = normalize(binormal_interp) * side; + vec3 tangent = normalize(tangent_interp) * side; #else vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif - vec3 normal = normalize(normal_interp)*side; - -#if defined(ENABLE_UV_INTERP) - vec2 uv = uv_interp; -#endif - -#if defined(ENABLE_UV2_INTERP) - vec2 uv2 = uv2_interp; -#endif - -#if defined(ENABLE_COLOR_INTERP) - vec4 color = color_interp; -#endif + vec3 normal = normalize(normal_interp) * side; #if defined(ENABLE_NORMALMAP) - - vec3 normalmap = vec3(0.0); + vec3 normalmap = vec3(0.5); #endif + float normaldepth = 1.0; - float normaldepth=1.0; -#if defined(SCREEN_UV_USED) - vec2 screen_uv = gl_FragCoord.xy*screen_pixel_size; +#ifdef ALPHA_SCISSOR_USED + float alpha_scissor = 0.5; #endif -#if defined (ENABLE_SSS) - float sss_strength=0.0; +#ifdef SCREEN_UV_USED + vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #endif { - FRAGMENT_SHADER_CODE -} +} -#if defined(ALPHA_SCISSOR_USED) - if (alpha<alpha_scissor) { - discard; - } -#endif - -#ifdef USE_OPAQUE_PREPASS +#if defined(ENABLE_NORMALMAP) + normalmap.xy = normalmap.xy * 2.0 - 1.0; + normalmap.z = sqrt(1.0 - dot(normalmap.xy, normalmap.xy)); - if (alpha<0.99) { - discard; - } + // normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; + normal = normalmap; #endif -#if defined(ENABLE_NORMALMAP) + normal = normalize(normal); - normalmap.xy=normalmap.xy*2.0-1.0; - normalmap.z=sqrt(1.0-dot(normalmap.xy,normalmap.xy)); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. + vec3 N = normal; - normal = normalize( mix(normal_interp,tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z,normaldepth) ) * side; + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); -#endif + vec3 ambient_light = vec3(0.0, 0.0, 0.0); -#if defined(LIGHT_USE_ANISOTROPY) - - if (anisotropy>0.01) { - //rotation matrix - mat3 rot = mat3( tangent, binormal, normal ); - //make local to space - tangent = normalize(rot * vec3(anisotropy_flow.x,anisotropy_flow.y,0.0)); - binormal = normalize(rot * vec3(-anisotropy_flow.y,anisotropy_flow.x,0.0)); - } + vec3 env_reflection_light = vec3(0.0, 0.0, 0.0); -#endif + vec3 eye_position = -normalize(vertex_interp); -#ifdef ENABLE_CLIP_ALPHA - if (albedo.a<0.99) { - //used for doublepass and shadowmapping +#ifdef ALPHA_SCISSOR_USED + if (alpha < alpha_scissor) { discard; } #endif -/////////////////////// LIGHTING ////////////////////////////// - - //apply energy conservation - -#ifdef USE_VERTEX_LIGHTING - - vec3 specular_light = specular_light_interp.rgb; - vec3 diffuse_light = diffuse_light_interp.rgb; -#else - - vec3 specular_light = vec3(0.0,0.0,0.0); - vec3 diffuse_light = vec3(0.0,0.0,0.0); - -#endif +// +// Lighting +// +#ifdef LIGHT_PASS - vec3 ambient_light; - vec3 env_reflection_light = vec3(0.0,0.0,0.0); + if (light_type == LIGHT_TYPE_OMNI) { + vec3 light_vec = light_position - vertex; + float light_length = length(light_vec); - vec3 eye_vec = -normalize( vertex_interp ); + float normalized_distance = light_length / light_range; + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + vec3 attenuation = vec3(omni_attenuation); -#ifdef USE_RADIANCE_MAP + if (light_has_shadow > 0.5) { + highp vec3 splane = (light_shadow_matrix * vec4(vertex, 1.0)).xyz; + float shadow_len = length(splane); - if (no_ambient_light) { - ambient_light=vec3(0.0,0.0,0.0); - } else { - { + splane = normalize(splane); - { //read radiance from dual paraboloid + vec4 clamp_rect = light_clamp; - vec3 ref_vec = reflect(-eye_vec,normal); //2.0 * ndotv * normal - view; // reflect(v, n); - ref_vec=normalize((radiance_inverse_xform * vec4(ref_vec,0.0)).xyz); - vec3 radiance = textureDualParaboloid(radiance_map,ref_vec,roughness) * bg_energy; - env_reflection_light = radiance; + if (splane.z >= 0.0) { + splane.z += 1.0; + clamp_rect.y += clamp_rect.w; + } else { + splane.z = 1.0 - splane.z; } - //no longer a cubemap - //vec3 radiance = textureLod(radiance_cube, r, lod).xyz * ( brdf.x + brdf.y); - } + splane.xy /= splane.z; + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len / light_range; - { + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; - vec3 ambient_dir=normalize((radiance_inverse_xform * vec4(normal,0.0)).xyz); - vec3 env_ambient=textureDualParaboloid(radiance_map,ambient_dir,1.0) * bg_energy; + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, clamp_rect); - ambient_light=mix(ambient_light_color.rgb,env_ambient,radiance_ambient_contribution); - //ambient_light=vec3(0.0,0.0,0.0); + if (shadow > splane.z) { + } else { + attenuation = vec3(0.0); + } } - } - -#else - - if (no_ambient_light){ - ambient_light=vec3(0.0,0.0,0.0); - } else { - ambient_light=ambient_light_color.rgb; - } -#endif - ambient_light*=ambient_energy; - - float specular_blob_intensity=1.0; -#if defined(SPECULAR_TOON) - specular_blob_intensity*=specular * 2.0; -#endif - -#if defined(USE_LIGHT_DIRECTIONAL) - - vec3 light_attenuation=vec3(1.0); - - float depth_z = -vertex.z; -#ifdef LIGHT_DIRECTIONAL_SHADOW + light_compute(normal, + normalize(light_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + attenuation, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); + + } else if (light_type == LIGHT_TYPE_DIRECTIONAL) { + + vec3 light_vec = -light_direction; + vec3 attenuation = vec3(1.0, 1.0, 1.0); + + float depth_z = -vertex.z; + + if (light_has_shadow > 0.5) { #ifdef LIGHT_USE_PSSM4 - if (depth_z < shadow_split_offsets.w) { + if (depth_z < light_split_offsets.w) { #elif defined(LIGHT_USE_PSSM2) - if (depth_z < shadow_split_offsets.y) { + if (depth_z < light_split_offsets.y) { #else - if (depth_z < shadow_split_offsets.x) { -#endif //LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.x) { +#endif - vec3 pssm_coord; - float pssm_fade=0.0; + vec3 pssm_coord; + float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND - float pssm_blend; - vec3 pssm_coord2; - bool use_blend=true; + float pssm_blend; + vec3 pssm_coord2; + bool use_blend = true; #endif - #ifdef LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; - if (depth_z < shadow_split_offsets.y) { - - if (depth_z < shadow_split_offsets.x) { - - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - - -#if defined(LIGHT_USE_PSSM_BLEND) - - splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif + } else { + highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; - } else { - - highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; -#if defined(LIGHT_USE_PSSM_BLEND) - splane=(shadow_matrix3 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #endif + } + } else { + if (depth_z < light_split_offsets.z) { - } - } else { - - - if (depth_z < shadow_split_offsets.z) { - - highp vec4 splane=(shadow_matrix3 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; + highp vec4 splane = (light_shadow_matrix3 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; #if defined(LIGHT_USE_PSSM_BLEND) - splane=(shadow_matrix4 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(shadow_split_offsets.y,shadow_split_offsets.z,depth_z); + splane = (light_shadow_matrix4 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); #endif - } else { + } else { - highp vec4 splane=(shadow_matrix4 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.z,shadow_split_offsets.w,depth_z); + highp vec4 splane = (light_shadow_matrix4 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) - use_blend=false; - + use_blend = false; #endif + } + } - } - } - - - -#endif //LIGHT_USE_PSSM4 +#endif // LIGHT_USE_PSSM4 #ifdef LIGHT_USE_PSSM2 + if (depth_z < light_split_offsets.x) { - if (depth_z < shadow_split_offsets.x) { - - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; -#if defined(LIGHT_USE_PSSM_BLEND) - - splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord2 = splane.xyz / splane.w; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif - - } else { - highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); -#if defined(LIGHT_USE_PSSM_BLEND) - use_blend=false; - + } else { + highp vec4 splane = (light_shadow_matrix2 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; #endif + } - } - -#endif //LIGHT_USE_PSSM2 +#endif // LIGHT_USE_PSSM2 #if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) - { //regular orthogonal - highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); - pssm_coord=splane.xyz/splane.w; - } + { + highp vec4 splane = (light_shadow_matrix1 * vec4(vertex, 1.0)); + pssm_coord = splane.xyz / splane.w; + } #endif + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord.xy, pssm_coord.z, light_clamp); - //one one sample - - float shadow = sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord.xy,pssm_coord.z,light_clamp); - -#if defined(LIGHT_USE_PSSM_BLEND) - - if (use_blend) { - shadow=mix(shadow, sample_shadow(directional_shadow,directional_shadow_pixel_size,pssm_coord2.xy,pssm_coord2.z,light_clamp),pssm_blend); - } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow = mix(shadow, sample_shadow(light_shadow_atlas, vec2(0.0), pssm_coord2.xy, pssm_coord2.z, light_clamp), pssm_blend); + } #endif -#ifdef USE_CONTACT_SHADOWS - if (shadow>0.01 && shadow_color_contact.a>0.0) { + attenuation *= shadow; - float contact_shadow = contact_shadow_compute(vertex,-light_direction_attenuation.xyz,shadow_color_contact.a); - shadow=min(shadow,contact_shadow); - } -#endif - light_attenuation=mix(mix(shadow_color_contact.rgb,vec3(1.0),shadow),vec3(1.0),pssm_fade); - - - } + } + } -#endif //LIGHT_DIRECTIONAL_SHADOW + light_compute(normal, + normalize(light_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + attenuation, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); + } else if (light_type == LIGHT_TYPE_SPOT) { + + vec3 light_att = vec3(1.0); + + if (light_has_shadow > 0.5) { + highp vec4 splane = (light_shadow_matrix * vec4(vertex, 1.0)); + splane.xyz /= splane.w; + + float shadow = sample_shadow(light_shadow_atlas, vec2(0.0), splane.xy, splane.z, light_clamp); + + if (shadow > splane.z) { + } else { + light_att = vec3(0.0); + } -#ifdef USE_VERTEX_LIGHTING - diffuse_light*=mix(vec3(1.0),light_attenuation,diffuse_light_interp.a); - specular_light*=mix(vec3(1.0),light_attenuation,specular_light_interp.a); -#else - light_compute(normal,-light_direction_attenuation.xyz,eye_vec,binormal,tangent,light_color_energy.rgb,light_attenuation,albedo,transmission,light_params.z*specular_blob_intensity,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); -#endif + } + vec3 light_rel_vec = light_position - vertex; + float light_length = length(light_rel_vec); + float normalized_distance = light_length / light_range; -#endif //#USE_LIGHT_DIRECTIONAL + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + vec3 spot_dir = light_direction; -#ifdef USE_GI_PROBES - gi_probes_compute(vertex,normal,roughness,env_reflection_light,ambient_light); + float spot_cutoff = light_spot_angle; -#endif + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); -#ifdef USE_FORWARD_LIGHTING + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + light_att *= vec3(spot_attenuation); - highp vec4 reflection_accum = vec4(0.0,0.0,0.0,0.0); - highp vec4 ambient_accum = vec4(0.0,0.0,0.0,0.0); - for(int i=0;i<reflection_count;i++) { - reflection_process(reflection_indices[i],vertex,normal,binormal,tangent,roughness,anisotropy,ambient_light,env_reflection_light,reflection_accum,ambient_accum); + light_compute(normal, + normalize(light_rel_vec), + eye_position, + binormal, + tangent, + light_color.xyz * light_energy, + light_att, + albedo, + transmission, + specular * light_specular, + roughness, + metallic, + rim, + rim_tint, + clearcoat, + clearcoat_gloss, + anisotropy, + diffuse_light, + specular_light); } - if (reflection_accum.a>0.0) { - specular_light+=reflection_accum.rgb/reflection_accum.a; - } else { - specular_light+=env_reflection_light; - } - - if (ambient_accum.a>0.0) { - ambient_light+=ambient_accum.rgb/ambient_accum.a; - } + gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); +#else +#ifdef RENDER_DEPTH +#else -#ifdef USE_VERTEX_LIGHTING +#ifdef USE_RADIANCE_MAP - diffuse_light*=albedo; -#else - for(int i=0;i<omni_light_count;i++) { - light_process_omni(omni_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light); - } + vec3 ref_vec = reflect(-eye_position, N); + ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); - for(int i=0;i<spot_light_count;i++) { - light_process_spot(spot_light_indices[i],vertex,eye_vec,normal,binormal,tangent,albedo,transmission,roughness,metallic,rim,rim_tint,clearcoat,clearcoat_gloss,anisotropy,specular_blob_intensity,diffuse_light,specular_light); - } + ref_vec.z *= -1.0; -#endif //USE_VERTEX_LIGHTING + env_reflection_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; -#endif + { + vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); + vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).xyz * bg_energy; + ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); + } + ambient_light *= ambient_energy; -#ifdef RENDER_DEPTH -//nothing happens, so a tree-ssa optimizer will result in no fragment shader :) -#else + specular_light += env_reflection_light; - specular_light*=reflection_multiplier; - ambient_light*=albedo; //ambient must be multiplied by albedo at the end + ambient_light *= albedo; #if defined(ENABLE_AO) - ambient_light*=ao; - ao_light_affect = mix(1.0,ao,ao_light_affect); - specular_light*=ao_light_affect; - diffuse_light*=ao_light_affect; + ambient_light *= ao; + ao_light_affect = mix(1.0, ao, ao_light_affect); + specular_light *= ao_light_affect; + diffuse_light *= ao_light_affect; #endif + diffuse_light *= 1.0 - metallic; + ambient_light *= 1.0 - metallic; + // environment BRDF approximation - //energy conservation - diffuse_light *= 1.0-metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point - ambient_light *= 1.0-metallic; - - + // TODO shadeless { - -#if defined(DIFFUSE_TOON) - //simplify for toon, as - specular_light *= specular * metallic * albedo * 2.0; -#else - // Environment brdf approximation (Lazarov 2013) - // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c1 = vec4( 1.0, 0.0425, 1.04, -0.04); vec4 r = roughness * c0 + c1; - float ndotv = clamp(dot(normal,eye_vec),0.0,1.0); + float ndotv = clamp(dot(normal,eye_position),0.0,1.0); float a004 = min( r.x * r.x, exp2( -9.28 * ndotv ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo); specular_light *= AB.x * specular_color + AB.y; -#endif - } - if (fog_color_enabled.a > 0.5) { - float fog_amount=0.0; + gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); + // gl_FragColor = vec4(normal, 1.0); - -#ifdef USE_LIGHT_DIRECTIONAL - - vec3 fog_color = mix( fog_color_enabled.rgb, fog_sun_color_amount.rgb,fog_sun_color_amount.a * pow(max( dot(normalize(vertex),-light_direction_attenuation.xyz), 0.0),8.0) ); #else - - vec3 fog_color = fog_color_enabled.rgb; + gl_FragColor = vec4(albedo, alpha); #endif - - //apply fog - - if (fog_depth_enabled) { - - float fog_z = smoothstep(fog_depth_begin,z_far,length(vertex)); - - fog_amount = pow(fog_z,fog_depth_curve); - if (fog_transmit_enabled) { - vec3 total_light = emission + ambient_light + specular_light + diffuse_light; - float transmit = pow(fog_z,fog_transmit_curve); - fog_color = mix(max(total_light,fog_color),fog_color,transmit); - } - } - - if (fog_height_enabled) { - float y = (camera_matrix * vec4(vertex,1.0)).y; - fog_amount = max(fog_amount,pow(smoothstep(fog_height_min,fog_height_max,y),fog_height_curve)); - } - - float rev_amount = 1.0 - fog_amount; - - - emission = emission * rev_amount + fog_color * fog_amount; - ambient_light*=rev_amount; - specular_light*rev_amount; - diffuse_light*=rev_amount; - - } - -#ifdef USE_MULTIPLE_RENDER_TARGETS - - -#ifdef SHADELESS - diffuse_buffer=vec4(albedo.rgb,0.0); - specular_buffer=vec4(0.0); - -#else - -#if defined(ENABLE_AO) - - float ambient_scale=0.0; // AO is supplied by material -#else - //approximate ambient scale for SSAO, since we will lack full ambient - float max_emission=max(emission.r,max(emission.g,emission.b)); - float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b)); - float max_diffuse=max(diffuse_light.r,max(diffuse_light.g,diffuse_light.b)); - float total_ambient = max_ambient+max_diffuse+max_emission; - float ambient_scale = (total_ambient>0.0) ? (max_ambient+ambient_occlusion_affect_light*max_diffuse)/total_ambient : 0.0; -#endif //ENABLE_AO - - diffuse_buffer=vec4(emission+diffuse_light+ambient_light,ambient_scale); - specular_buffer=vec4(specular_light,metallic); - -#endif //SHADELESS - - normal_mr_buffer=vec4(normalize(normal)*0.5+0.5,roughness); - -#if defined (ENABLE_SSS) - sss_buffer = sss_strength; -#endif - - -#else //USE_MULTIPLE_RENDER_TARGETS - - -#ifdef SHADELESS - frag_color=vec4(albedo,alpha); -#else - frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha); -#endif //SHADELESS - - -#endif //USE_MULTIPLE_RENDER_TARGETS - +#endif // RENDER_DEPTH -#endif //RENDER_DEPTH +#endif // lighting } diff --git a/drivers/gles2/shaders/stdlib.glsl b/drivers/gles2/shaders/stdlib.glsl new file mode 100644 index 0000000000..ebbdb96311 --- /dev/null +++ b/drivers/gles2/shaders/stdlib.glsl @@ -0,0 +1,45 @@ + +vec2 select2(vec2 a, vec2 b, bvec2 c) +{ + vec2 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + + return ret; +} + +vec3 select3(vec3 a, vec3 b, bvec3 c) +{ + vec3 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + ret.z = c.z ? b.z : a.z; + + return ret; +} + +vec4 select4(vec4 a, vec4 b, bvec4 c) +{ + vec4 ret; + + ret.x = c.x ? b.x : a.x; + ret.y = c.y ? b.y : a.y; + ret.z = c.z ? b.z : a.z; + ret.w = c.w ? b.w : a.w; + + return ret; +} + + +highp vec4 texel2DFetch(highp sampler2D tex, ivec2 size, ivec2 coord) +{ + float x_coord = float(2 * coord.x + 1) / float(size.x * 2); + float y_coord = float(2 * coord.y + 1) / float(size.y * 2); + + x_coord = float(coord.x) / float(size.x); + y_coord = float(coord.y) / float(size.y); + + return texture2DLod(tex, vec2(x_coord, y_coord), 0.0); +} diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 1abdaa5f80..cb17695c5f 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -192,22 +192,15 @@ void RasterizerGLES3::initialize() { scene->initialize(); } -void RasterizerGLES3::begin_frame() { +void RasterizerGLES3::begin_frame(double frame_step) { - uint64_t tick = OS::get_singleton()->get_ticks_usec(); + time_total += frame_step; - double delta = double(tick - prev_ticks) / 1000000.0; - delta *= Engine::get_singleton()->get_time_scale(); - - time_total += delta; - - if (delta == 0) { + if (frame_step == 0) { //to avoid hiccups - delta = 0.001; + frame_step = 0.001; } - prev_ticks = tick; - double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); if (time_total > time_roll_over) time_total = 0; //roll over every day (should be customz @@ -217,9 +210,7 @@ void RasterizerGLES3::begin_frame() { storage->frame.time[2] = Math::fmod(time_total, 900); storage->frame.time[3] = Math::fmod(time_total, 60); storage->frame.count++; - storage->frame.delta = delta; - - storage->frame.prev_tick = tick; + storage->frame.delta = frame_step; storage->update_dirty_resources(); @@ -234,7 +225,7 @@ void RasterizerGLES3::set_current_render_target(RID p_render_target) { if (!p_render_target.is_valid() && storage->frame.current_rt && storage->frame.clear_request) { //handle pending clear request, if the framebuffer was not cleared glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); - print_line("unbind clear of: " + storage->frame.clear_request_color); + glClearColor( storage->frame.clear_request_color.r, storage->frame.clear_request_color.g, @@ -281,7 +272,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c if (p_image.is_null() || p_image->empty()) return; - begin_frame(); + begin_frame(0.0); int window_w = OS::get_singleton()->get_video_mode(0).width; int window_h = OS::get_singleton()->get_video_mode(0).height; @@ -299,7 +290,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c canvas->canvas_begin(); RID texture = storage->texture_create(); - storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), VS::TEXTURE_FLAG_FILTER); + storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); storage->texture_set_data(texture, p_image); Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); @@ -333,28 +324,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c storage->free(texture); // free since it's only one frame that stays there - if (OS::get_singleton()->is_layered_allowed()) { - if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { -#if (defined WINDOWS_ENABLED) && !(defined UWP_ENABLED) - Size2 wndsize = OS::get_singleton()->get_layered_buffer_size(); - uint8_t *data = OS::get_singleton()->get_layered_buffer_data(); - if (data) { - glReadPixels(0, 0, wndsize.x, wndsize.y, GL_BGRA, GL_UNSIGNED_BYTE, data); - OS::get_singleton()->swap_layered_buffer(); - - return; - } -#endif - } else { - //clear alpha - glColorMask(false, false, false, true); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glColorMask(true, true, true, true); - } - } - - OS::get_singleton()->swap_buffers(); + end_frame(true); } void RasterizerGLES3::blit_render_target_to_screen(RID p_render_target, const Rect2 &p_screen_rect, int p_screen) { @@ -451,7 +421,6 @@ RasterizerGLES3::RasterizerGLES3() { scene->storage = storage; storage->scene = scene; - prev_ticks = 0; time_total = 0; } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 5213101778..f4449ac0f9 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -44,7 +44,6 @@ class RasterizerGLES3 : public Rasterizer { RasterizerCanvasGLES3 *canvas; RasterizerSceneGLES3 *scene; - uint64_t prev_ticks; double time_total; public: @@ -55,7 +54,7 @@ public: virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale); virtual void initialize(); - virtual void begin_frame(); + virtual void begin_frame(double frame_step); virtual void set_current_render_target(RID p_render_target); virtual void restore_render_target(); virtual void clear_render_target(const Color &p_color); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 85b63a6481..eebdbe9493 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -355,7 +355,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version; if (!should_realloc) { - shadow_atlas->quadrants[q].shadows[s].version = p_light_version; + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; //already existing, see if it should redraw or it's just OK return should_redraw; } @@ -365,7 +365,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //find a better place if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) { //found a better place! - ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows[new_shadow]; + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; if (sh->owner.is_valid()) { //is taken, but is invalid, erasing it shadow_atlas->shadow_owners.erase(sh->owner); @@ -374,8 +374,8 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in } //erase previous - shadow_atlas->quadrants[q].shadows[s].version = 0; - shadow_atlas->quadrants[q].shadows[s].owner = RID(); + shadow_atlas->quadrants[q].shadows.write[s].version = 0; + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); sh->owner = p_light_intance; sh->alloc_tick = tick; @@ -395,7 +395,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //already existing, see if it should redraw or it's just OK - shadow_atlas->quadrants[q].shadows[s].version = p_light_version; + shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version; return should_redraw; } @@ -405,7 +405,7 @@ bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_in //find a better place if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) { //found a better place! - ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows[new_shadow]; + ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow]; if (sh->owner.is_valid()) { //is taken, but is invalid, erasing it shadow_atlas->shadow_owners.erase(sh->owner); @@ -502,7 +502,7 @@ void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_size //erase probes reference to this if (reflection_atlas->reflections[i].owner.is_valid()) { ReflectionProbeInstance *reflection_probe_instance = reflection_probe_instance_owner.getornull(reflection_atlas->reflections[i].owner); - reflection_atlas->reflections[i].owner = RID(); + reflection_atlas->reflections.write[i].owner = RID(); ERR_CONTINUE(!reflection_probe_instance); reflection_probe_instance->reflection_atlas_index = -1; @@ -574,7 +574,7 @@ void RasterizerSceneGLES3::reflection_atlas_set_subdivision(RID p_ref_atlas, int //erase probes reference to this if (reflection_atlas->reflections[i].owner.is_valid()) { ReflectionProbeInstance *reflection_probe_instance = reflection_probe_instance_owner.getornull(reflection_atlas->reflections[i].owner); - reflection_atlas->reflections[i].owner = RID(); + reflection_atlas->reflections.write[i].owner = RID(); ERR_CONTINUE(!reflection_probe_instance); reflection_probe_instance->reflection_atlas_index = -1; @@ -629,7 +629,7 @@ void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) ERR_FAIL_COND(reflection_atlas->reflections[rpi->reflection_atlas_index].owner != rpi->self); - reflection_atlas->reflections[rpi->reflection_atlas_index].owner = RID(); + reflection_atlas->reflections.write[rpi->reflection_atlas_index].owner = RID(); rpi->reflection_atlas_index = -1; rpi->atlas = RID(); @@ -701,8 +701,8 @@ bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance victim_rpi->reflection_atlas_index = -1; } - reflection_atlas->reflections[best_free].owner = p_instance; - reflection_atlas->reflections[best_free].last_frame = storage->frame.count; + reflection_atlas->reflections.write[best_free].owner = p_instance; + reflection_atlas->reflections.write[best_free].last_frame = storage->frame.count; rpi->reflection_atlas_index = best_free; rpi->atlas = p_reflection_atlas; @@ -1190,6 +1190,7 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m int tc = p_material->textures.size(); RID *textures = p_material->textures.ptrw(); ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw(); + const ShaderLanguage::DataType *texture_types = p_material->shader->texture_types.ptr(); state.current_main_tex = 0; @@ -1198,39 +1199,17 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m glActiveTexture(GL_TEXTURE0 + i); GLenum target; - GLuint tex; + GLuint tex = 0; - RasterizerStorageGLES3::Texture *t = storage->texture_owner.getornull(textures[i]); + RasterizerStorageGLES3::Texture *t = storage->texture_owner.getptr(textures[i]); - if (!t) { - //check hints - target = GL_TEXTURE_2D; - - switch (texture_hints[i]) { - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { - tex = storage->resources.black_tex; - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { - tex = storage->resources.aniso_tex; - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { - tex = storage->resources.normal_tex; - - } break; - default: { - tex = storage->resources.white_tex; - } break; - } - - } else { + if (t) { if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies VisualServerRaster::redraw_request(); } t = t->get_ptr(); //resolve for proxies - #ifdef TOOLS_ENABLED if (t->detect_3d) { t->detect_3d(t->detect_3d_ud); @@ -1247,6 +1226,59 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m target = t->target; tex = t->tex_id; + } else { + + switch (texture_types[i]) { + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER2D: { + target = GL_TEXTURE_2D; + + switch (texture_hints[i]) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + tex = storage->resources.black_tex; + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + tex = storage->resources.aniso_tex; + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + tex = storage->resources.normal_tex; + + } break; + default: { + tex = storage->resources.white_tex; + } break; + } + + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + // TODO + } break; + + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER3D: { + + target = GL_TEXTURE_3D; + + switch (texture_hints[i]) { + + // TODO + default: { + tex = storage->resources.white_tex_3d; + } break; + } + + } break; + + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: { + // TODO + } break; + } } glBindTexture(target, tex); @@ -3848,8 +3880,8 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.exposure_shader.set_conditional(ExposureShaderGLES3::EXPOSURE_END, false); //last step, swap with the framebuffer exposure, so the right exposure is kept int he framebuffer - SWAP(exposure_shrink[exposure_shrink.size() - 1].fbo, storage->frame.current_rt->exposure.fbo); - SWAP(exposure_shrink[exposure_shrink.size() - 1].color, storage->frame.current_rt->exposure.color); + SWAP(exposure_shrink.write[exposure_shrink.size() - 1].fbo, storage->frame.current_rt->exposure.fbo); + SWAP(exposure_shrink.write[exposure_shrink.size() - 1].color, storage->frame.current_rt->exposure.color); glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height); @@ -4086,6 +4118,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const state.ubo_data.z_slope_scale = 0; state.ubo_data.shadow_dual_paraboloid_render_side = 0; state.ubo_data.shadow_dual_paraboloid_render_zfar = 0; + state.ubo_data.opaque_prepass_threshold = 0.99; p_cam_projection.get_viewport_size(state.ubo_data.viewport_size[0], state.ubo_data.viewport_size[1]); @@ -4698,6 +4731,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ state.ubo_data.z_slope_scale = normal_bias; state.ubo_data.shadow_dual_paraboloid_render_side = dp_direction; state.ubo_data.shadow_dual_paraboloid_render_zfar = zfar; + state.ubo_data.opaque_prepass_threshold = 0.1; _setup_environment(NULL, light_projection, light_transform); @@ -4776,7 +4810,7 @@ bool RasterizerSceneGLES3::free(RID p_rid) { uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; - shadow_atlas->quadrants[q].shadows[s].owner = RID(); + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); shadow_atlas->shadow_owners.erase(p_rid); } diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 524212b9c1..cf387a69bc 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -141,6 +141,7 @@ public: float subsurface_scatter_width; float ambient_occlusion_affect_light; float ambient_occlusion_affect_ssao; + float opaque_prepass_threshold; uint32_t fog_depth_enabled; float fog_depth_begin; @@ -152,7 +153,7 @@ public: float fog_height_max; float fog_height_curve; // make sure this struct is padded to be a multiple of 16 bytes for webgl - float pad[3]; + float pad[2]; } ubo_data; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index e67b0bea21..a5c81d6c4d 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -595,7 +595,7 @@ RID RasterizerStorageGLES3::texture_create() { return texture_owner.make_rid(texture); } -void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { +void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VisualServer::TextureType p_type, uint32_t p_flags) { GLenum format; GLenum internal_format; @@ -612,15 +612,37 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ ERR_FAIL_COND(!texture); texture->width = p_width; texture->height = p_height; + texture->depth = p_depth_3d; texture->format = p_format; texture->flags = p_flags; texture->stored_cube_sides = 0; - texture->target = (p_flags & VS::TEXTURE_FLAG_CUBEMAP) ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D; + + texture->type = p_type; + + switch (p_type) { + case VS::TEXTURE_TYPE_2D: { + texture->target = GL_TEXTURE_2D; + texture->images.resize(1); + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + texture->target = GL_TEXTURE_CUBE_MAP; + texture->images.resize(6); + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + texture->target = GL_TEXTURE_2D_ARRAY; + texture->images.resize(p_depth_3d); + } break; + case VS::TEXTURE_TYPE_3D: { + texture->target = GL_TEXTURE_3D; + texture->images.resize(p_depth_3d); + } break; + } _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, format, internal_format, type, compressed, srgb); texture->alloc_width = texture->width; texture->alloc_height = texture->height; + texture->alloc_depth = texture->depth; texture->gl_format_cache = format; texture->gl_type_cache = type; @@ -633,7 +655,34 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + if (p_type == VS::TEXTURE_TYPE_3D || p_type == VS::TEXTURE_TYPE_2D_ARRAY) { + + int width = p_width; + int height = p_height; + int depth = p_depth_3d; + + int mipmaps = 0; + + while (width != 1 && height != 1) { + glTexImage3D(texture->target, 0, internal_format, width, height, depth, 0, format, type, NULL); + + width = MAX(1, width / 2); + height = MAX(1, height / 2); + + if (p_type == VS::TEXTURE_TYPE_3D) { + depth = MAX(1, depth / 2); + } + + mipmaps++; + + if (!(p_flags & VS::TEXTURE_FLAG_MIPMAPS)) + break; + } + + glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + + } else if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { //prealloc if video glTexImage2D(texture->target, 0, internal_format, p_width, p_height, 0, format, type, NULL); } @@ -641,7 +690,7 @@ void RasterizerStorageGLES3::texture_allocate(RID p_texture, int p_width, int p_ texture->active = true; } -void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.get(p_texture); @@ -658,7 +707,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p bool srgb; if (config.keep_original_textures && !(texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images[p_cube_side] = p_image; + texture->images.write[p_layer] = p_image; } Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, format, internal_format, type, compressed, srgb); @@ -677,7 +726,23 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p } }; - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target; + + switch (texture->type) { + case VS::TEXTURE_TYPE_2D: { + blit_target = GL_TEXTURE_2D; + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + ERR_FAIL_INDEX(p_layer, 6); + blit_target = _cube_side_enum[p_layer]; + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + blit_target = GL_TEXTURE_2D_ARRAY; + } break; + case VS::TEXTURE_TYPE_3D: { + blit_target = GL_TEXTURE_3D; + } break; + } texture->data_size = img->get_data().size(); PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -785,20 +850,36 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p //print_line("mipmap: "+itos(i)+" size: "+itos(size)+" w: "+itos(mm_w)+", h: "+itos(mm_h)); - if (texture->compressed) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + if (texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) { - int bw = w; - int bh = h; + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + int bw = w; + int bh = h; + glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { + glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } + } } else { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + glCompressedTexSubImage3D(blit_target, i, 0, 0, p_layer, bw, bh, 1, internal_format, size, &read[ofs]); } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexSubImage3D(blit_target, i, 0, 0, p_layer, w, h, 1, format, type, &read[ofs]); } } tsize += size; @@ -813,14 +894,17 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p //printf("texture: %i x %i - size: %i - total: %i\n",texture->width,texture->height,tsize,_rinfo.texture_mem); - texture->stored_cube_sides |= (1 << p_cube_side); + texture->stored_cube_sides |= (1 << p_layer); - if ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP) || texture->stored_cube_sides == (1 << 6) - 1)) { + if ((texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) && (texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != VS::TEXTURE_TYPE_CUBEMAP || texture->stored_cube_sides == (1 << 6) - 1)) { //generate mipmaps if they were requested and the image does not contain them glGenerateMipmap(texture->target); } else if (mipmaps > 1) { glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1); + } else { + glTexParameteri(texture->target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(texture->target, GL_TEXTURE_MAX_LEVEL, 0); } texture->mipmaps = mipmaps; @@ -831,7 +915,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p // Uploads pixel data to a sub-region of a texture, for the specified mipmap. // The texture pixels must have been allocated before, because most features seen in texture_set_data() make no sense in a partial update. // TODO If we want this to be usable without pre-filling pixels with a full image, we have to call glTexImage2D() with null data. -void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { +void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { Texture *texture = texture_owner.get(p_texture); @@ -859,7 +943,23 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, format, internal_format, type, compressed, srgb); - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + GLenum blit_target; + + switch (texture->type) { + case VS::TEXTURE_TYPE_2D: { + blit_target = GL_TEXTURE_2D; + } break; + case VS::TEXTURE_TYPE_CUBEMAP: { + ERR_FAIL_INDEX(p_layer, 6); + blit_target = _cube_side_enum[p_layer]; + } break; + case VS::TEXTURE_TYPE_2D_ARRAY: { + blit_target = GL_TEXTURE_2D_ARRAY; + } break; + case VS::TEXTURE_TYPE_3D: { + blit_target = GL_TEXTURE_3D; + } break; + } PoolVector<uint8_t>::Read read = img->get_data().read(); @@ -869,18 +969,38 @@ void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<I int src_data_size = img->get_data().size(); int src_ofs = 0; - if (texture->compressed) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]); + if (texture->type == VS::TEXTURE_TYPE_2D || texture->type == VS::TEXTURE_TYPE_CUBEMAP) { + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // `format` has to match the internal_format used when the texture was created + glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]); + } + } else { + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexSubImage3D(blit_target, p_dst_mip, dst_x, dst_y, p_layer, src_w, src_h, 1, format, src_data_size, &read[src_ofs]); + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // `format` has to match the internal_format used when the texture was created + glTexSubImage3D(blit_target, p_dst_mip, dst_x, dst_y, p_layer, src_w, src_h, 1, format, type, &read[src_ofs]); + } + } + + if (texture->flags & VS::TEXTURE_FLAG_FILTER) { + + glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtering } else { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // `format` has to match the internal_format used when the texture was created - glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]); + + glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // raw Filtering } } -Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { +Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) const { Texture *texture = texture_owner.get(p_texture); @@ -888,8 +1008,8 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSi ERR_FAIL_COND_V(!texture->active, Ref<Image>()); ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); - if (!texture->images[p_cube_side].is_null()) { - return texture->images[p_cube_side]; + if (texture->type == VS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && !texture->images[p_layer].is_null()) { + return texture->images[p_layer]; } #ifdef GLES_OVER_GL @@ -977,10 +1097,10 @@ void RasterizerStorageGLES3::texture_set_flags(RID p_texture, uint32_t p_flags) bool had_mipmaps = texture->flags & VS::TEXTURE_FLAG_MIPMAPS; + texture->flags = p_flags; + glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - uint32_t cube = texture->flags & VS::TEXTURE_FLAG_CUBEMAP; - texture->flags = p_flags | cube; // can't remove a cube from being a cube if (((texture->flags & VS::TEXTURE_FLAG_REPEAT) || (texture->flags & VS::TEXTURE_FLAG_MIRRORED_REPEAT)) && texture->target != GL_TEXTURE_CUBE_MAP) { @@ -1058,6 +1178,14 @@ Image::Format RasterizerStorageGLES3::texture_get_format(RID p_texture) const { return texture->format; } + +VisualServer::TextureType RasterizerStorageGLES3::texture_get_type(RID p_texture) const { + Texture *texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture, VS::TEXTURE_TYPE_2D); + + return texture->type; +} uint32_t RasterizerStorageGLES3::texture_get_texid(RID p_texture) const { Texture *texture = texture_owner.get(p_texture); @@ -1083,7 +1211,16 @@ uint32_t RasterizerStorageGLES3::texture_get_height(RID p_texture) const { return texture->height; } -void RasterizerStorageGLES3::texture_set_size_override(RID p_texture, int p_width, int p_height) { +uint32_t RasterizerStorageGLES3::texture_get_depth(RID p_texture) const { + + Texture *texture = texture_owner.get(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void RasterizerStorageGLES3::texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth) { Texture *texture = texture_owner.get(p_texture); @@ -1123,8 +1260,9 @@ void RasterizerStorageGLES3::texture_debug_usage(List<VS::TextureInfo> *r_info) VS::TextureInfo tinfo; tinfo.path = t->path; tinfo.format = t->format; - tinfo.size.x = t->alloc_width; - tinfo.size.y = t->alloc_height; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; tinfo.bytes = t->total_data_size; r_info->push_back(tinfo); } @@ -1169,7 +1307,7 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ Texture *texture = texture_owner.get(p_source); ERR_FAIL_COND_V(!texture, RID()); - ERR_FAIL_COND_V(!(texture->flags & VS::TEXTURE_FLAG_CUBEMAP), RID()); + ERR_FAIL_COND_V(texture->type != VS::TEXTURE_TYPE_CUBEMAP, RID()); bool use_float = config.hdr_supported; @@ -1285,7 +1423,8 @@ RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_ Texture *ctex = memnew(Texture); - ctex->flags = VS::TEXTURE_FLAG_CUBEMAP | VS::TEXTURE_FLAG_MIPMAPS | VS::TEXTURE_FLAG_FILTER; + ctex->type = VS::TEXTURE_TYPE_CUBEMAP; + ctex->flags = VS::TEXTURE_FLAG_MIPMAPS | VS::TEXTURE_FLAG_FILTER; ctex->width = p_resolution; ctex->height = p_resolution; ctex->alloc_width = p_resolution; @@ -1765,6 +1904,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { p_shader->ubo_offsets = gen_code.uniform_offsets; p_shader->texture_count = gen_code.texture_uniforms.size(); p_shader->texture_hints = gen_code.texture_hints; + p_shader->texture_types = gen_code.texture_types; p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time; @@ -1875,6 +2015,13 @@ void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Texture"; } break; + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture3D"; + } break; case ShaderLanguage::TYPE_SAMPLERCUBE: { pi.type = Variant::OBJECT; @@ -2649,6 +2796,7 @@ void RasterizerStorageGLES3::_update_material(Material *material) { //set up the texture array, for easy access when it needs to be drawn if (material->shader && material->shader->texture_count) { + material->texture_is_3d.resize(material->shader->texture_count); material->textures.resize(material->shader->texture_count); for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { @@ -2658,6 +2806,16 @@ void RasterizerStorageGLES3::_update_material(Material *material) { RID texture; + switch (E->get().type) { + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: { + material->texture_is_3d.write[E->get().texture_order] = true; + } break; + default: { + material->texture_is_3d.write[E->get().texture_order] = false; + } break; + } + Map<StringName, Variant>::Element *V = material->params.find(E->key()); if (V) { texture = V->get(); @@ -2670,11 +2828,12 @@ void RasterizerStorageGLES3::_update_material(Material *material) { } } - material->textures[E->get().texture_order] = texture; + material->textures.write[E->get().texture_order] = texture; } } else { material->textures.clear(); + material->texture_is_3d.clear(); } } @@ -2975,9 +3134,9 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS: for (int i = 0; i < surface->skeleton_bone_used.size(); i++) { if (surface->skeleton_bone_aabb[i].size.x < 0 || surface->skeleton_bone_aabb[i].size.y < 0 || surface->skeleton_bone_aabb[i].size.z < 0) { - surface->skeleton_bone_used[i] = false; + surface->skeleton_bone_used.write[i] = false; } else { - surface->skeleton_bone_used[i] = true; + surface->skeleton_bone_used.write[i] = true; } } @@ -3240,7 +3399,7 @@ void RasterizerStorageGLES3::mesh_surface_update_region(RID p_mesh, int p_surfac PoolVector<uint8_t>::Read r = p_data.read(); - glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->array_id); + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_id); glBufferSubData(GL_ARRAY_BUFFER, p_offset, total_size, r.ptr()); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } @@ -3404,6 +3563,7 @@ Vector<PoolVector<uint8_t> > RasterizerStorageGLES3::mesh_surface_get_blend_shap return bsarr; } + Vector<AABB> RasterizerStorageGLES3::mesh_surface_get_skeleton_aabb(RID p_mesh, int p_surface) const { const Mesh *mesh = mesh_owner.getornull(p_mesh); @@ -3455,6 +3615,7 @@ void RasterizerStorageGLES3::mesh_remove_surface(RID p_mesh, int p_surface) { mesh->instance_change_notify(); } + int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const { const Mesh *mesh = mesh_owner.getornull(p_mesh); @@ -3468,6 +3629,7 @@ void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb ERR_FAIL_COND(!mesh); mesh->custom_aabb = p_aabb; + mesh->instance_change_notify(); } AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const { @@ -3875,29 +4037,29 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances int custom_data_from = 0; if (multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D) { - multimesh->data[i + 0] = 1.0; - multimesh->data[i + 1] = 0.0; - multimesh->data[i + 2] = 0.0; - multimesh->data[i + 3] = 0.0; - multimesh->data[i + 4] = 0.0; - multimesh->data[i + 5] = 1.0; - multimesh->data[i + 6] = 0.0; - multimesh->data[i + 7] = 0.0; + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; color_from = 8; custom_data_from = 8; } else { - multimesh->data[i + 0] = 1.0; - multimesh->data[i + 1] = 0.0; - multimesh->data[i + 2] = 0.0; - multimesh->data[i + 3] = 0.0; - multimesh->data[i + 4] = 0.0; - multimesh->data[i + 5] = 1.0; - multimesh->data[i + 6] = 0.0; - multimesh->data[i + 7] = 0.0; - multimesh->data[i + 8] = 0.0; - multimesh->data[i + 9] = 0.0; - multimesh->data[i + 10] = 1.0; - multimesh->data[i + 11] = 0.0; + multimesh->data.write[i + 0] = 1.0; + multimesh->data.write[i + 1] = 0.0; + multimesh->data.write[i + 2] = 0.0; + multimesh->data.write[i + 3] = 0.0; + multimesh->data.write[i + 4] = 0.0; + multimesh->data.write[i + 5] = 1.0; + multimesh->data.write[i + 6] = 0.0; + multimesh->data.write[i + 7] = 0.0; + multimesh->data.write[i + 8] = 0.0; + multimesh->data.write[i + 9] = 0.0; + multimesh->data.write[i + 10] = 1.0; + multimesh->data.write[i + 11] = 0.0; color_from = 12; custom_data_from = 12; } @@ -3912,14 +4074,14 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0xFFFFFFFF; - multimesh->data[i + color_from + 0] = cu.colf; + multimesh->data.write[i + color_from + 0] = cu.colf; custom_data_from = color_from + 1; } else if (multimesh->color_format == VS::MULTIMESH_COLOR_FLOAT) { - multimesh->data[i + color_from + 0] = 1.0; - multimesh->data[i + color_from + 1] = 1.0; - multimesh->data[i + color_from + 2] = 1.0; - multimesh->data[i + color_from + 3] = 1.0; + multimesh->data.write[i + color_from + 0] = 1.0; + multimesh->data.write[i + color_from + 1] = 1.0; + multimesh->data.write[i + color_from + 2] = 1.0; + multimesh->data.write[i + color_from + 3] = 1.0; custom_data_from = color_from + 4; } @@ -3933,13 +4095,13 @@ void RasterizerStorageGLES3::multimesh_allocate(RID p_multimesh, int p_instances } cu; cu.colu = 0; - multimesh->data[i + custom_data_from + 0] = cu.colf; + multimesh->data.write[i + custom_data_from + 0] = cu.colf; } else if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_FLOAT) { - multimesh->data[i + custom_data_from + 0] = 0.0; - multimesh->data[i + custom_data_from + 1] = 0.0; - multimesh->data[i + custom_data_from + 2] = 0.0; - multimesh->data[i + custom_data_from + 3] = 0.0; + multimesh->data.write[i + custom_data_from + 0] = 0.0; + multimesh->data.write[i + custom_data_from + 1] = 0.0; + multimesh->data.write[i + custom_data_from + 2] = 0.0; + multimesh->data.write[i + custom_data_from + 3] = 0.0; } } @@ -4001,7 +4163,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform(RID p_multimesh, i ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; dataptr[0] = p_transform.basis.elements[0][0]; dataptr[1] = p_transform.basis.elements[0][1]; @@ -4032,7 +4194,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_transform_2d(RID p_multimesh ERR_FAIL_COND(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; dataptr[0] = p_transform.elements[0][0]; dataptr[1] = p_transform.elements[1][0]; @@ -4058,7 +4220,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p ERR_FAIL_COND(multimesh->color_format == VS::MULTIMESH_COLOR_NONE); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { @@ -4091,7 +4253,7 @@ void RasterizerStorageGLES3::multimesh_instance_set_custom_data(RID p_multimesh, ERR_FAIL_COND(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { @@ -4131,7 +4293,7 @@ Transform RasterizerStorageGLES3::multimesh_instance_get_transform(RID p_multime ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_2D, Transform()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; Transform xform; @@ -4158,7 +4320,7 @@ Transform2D RasterizerStorageGLES3::multimesh_instance_get_transform_2d(RID p_mu ERR_FAIL_COND_V(multimesh->transform_format == VS::MULTIMESH_TRANSFORM_3D, Transform2D()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index]; + float *dataptr = &multimesh->data.write[stride * p_index]; Transform2D xform; @@ -4180,7 +4342,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int ERR_FAIL_COND_V(multimesh->color_format == VS::MULTIMESH_COLOR_NONE, Color()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats]; if (multimesh->color_format == VS::MULTIMESH_COLOR_8BIT) { union { @@ -4213,7 +4375,7 @@ Color RasterizerStorageGLES3::multimesh_instance_get_custom_data(RID p_multimesh ERR_FAIL_COND_V(multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_NONE, Color()); int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; - float *dataptr = &multimesh->data[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; + float *dataptr = &multimesh->data.write[stride * p_index + multimesh->xform_floats + multimesh->color_floats]; if (multimesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { union { @@ -5769,7 +5931,7 @@ void RasterizerStorageGLES3::particles_set_draw_pass_mesh(RID p_particles, int p Particles *particles = particles_owner.getornull(p_particles); ERR_FAIL_COND(!particles); ERR_FAIL_INDEX(p_pass, particles->draw_passes.size()); - particles->draw_passes[p_pass] = p_mesh; + particles->draw_passes.write[p_pass] = p_mesh; } void RasterizerStorageGLES3::particles_restart(RID p_particles) { @@ -6643,7 +6805,7 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { for (int j = 0; j < rt->effects.mip_maps[i].sizes.size(); j++) { - RenderTarget::Effects::MipMaps::Size &mm = rt->effects.mip_maps[i].sizes[j]; + RenderTarget::Effects::MipMaps::Size &mm = rt->effects.mip_maps[i].sizes.write[j]; glGenFramebuffers(1, &mm.fbo); glBindFramebuffer(GL_FRAMEBUFFER, mm.fbo); @@ -6996,6 +7158,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { info.texture_mem -= texture->total_data_size; texture_owner.free(p_rid); memdelete(texture); + } else if (sky_owner.owns(p_rid)) { // delete the sky Sky *sky = sky_owner.get(p_rid); @@ -7055,7 +7218,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { for (int i = 0; i < ins->materials.size(); i++) { if (ins->materials[i] == p_rid) { - ins->materials[i] = RID(); + ins->materials.write[i] = RID(); } } } @@ -7405,6 +7568,15 @@ void RasterizerStorageGLES3::initialize() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, anisotexdata); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); + + glGenTextures(1, &resources.white_tex_3d); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_3D, resources.white_tex_3d); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, 2, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, whitetexdata); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0); } glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); @@ -7486,7 +7658,6 @@ void RasterizerStorageGLES3::initialize() { #endif frame.count = 0; - frame.prev_tick = 0; frame.delta = 0; frame.current_rt = NULL; config.keep_original_textures = false; diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 80df21941b..f55c8026ea 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -123,6 +123,8 @@ public: GLuint normal_tex; GLuint aniso_tex; + GLuint white_tex_3d; + GLuint quadie; GLuint quadie_array; @@ -248,9 +250,10 @@ public: String path; uint32_t flags; - int width, height; - int alloc_width, alloc_height; + int width, height, depth; + int alloc_width, alloc_height, alloc_depth; Image::Format format; + VS::TextureType type; GLenum target; GLenum gl_format_cache; @@ -274,7 +277,7 @@ public: RenderTarget *render_target; - Ref<Image> images[6]; + Vector<Ref<Image> > images; VisualServer::TextureDetectCallback detect_3d; void *detect_3d_ud; @@ -340,17 +343,19 @@ public: Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &srgb); virtual RID texture_create(); - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); - virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; + virtual void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, VS::TextureType p_type, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer = 0); + virtual Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; virtual Image::Format texture_get_format(RID p_texture) const; + virtual VS::TextureType texture_get_type(RID p_texture) const; virtual uint32_t texture_get_texid(RID p_texture) const; virtual uint32_t texture_get_width(RID p_texture) const; virtual uint32_t texture_get_height(RID p_texture) const; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height); + virtual uint32_t texture_get_depth(RID p_texture) const; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth); virtual void texture_set_path(RID p_texture, const String &p_path); virtual String texture_get_path(RID p_texture) const; @@ -410,6 +415,7 @@ public: Map<StringName, RID> default_textures; + Vector<ShaderLanguage::DataType> texture_types; Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; bool valid; @@ -532,6 +538,7 @@ public: Map<StringName, Variant> params; SelfList<Material> list; SelfList<Material> dirty_list; + Vector<bool> texture_is_3d; Vector<RID> textures; float line_width; int render_priority; @@ -693,7 +700,6 @@ public: AABB custom_aabb; mutable uint64_t last_pass; SelfList<MultiMesh>::List multimeshes; - _FORCE_INLINE_ void update_multimeshes() { SelfList<MultiMesh> *mm = multimeshes.first(); @@ -1428,7 +1434,6 @@ public: int canvas_draw_commands; float time[4]; float delta; - uint64_t prev_tick; uint64_t count; } frame; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index f3ba7aa408..0c353d42bb 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -341,6 +341,7 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener r_gen_code.texture_uniforms.resize(max_texture_uniforms); r_gen_code.texture_hints.resize(max_texture_uniforms); + r_gen_code.texture_types.resize(max_texture_uniforms); Vector<int> uniform_sizes; Vector<int> uniform_alignments; @@ -365,17 +366,18 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener if (SL::is_sampler_type(E->get().type)) { r_gen_code.vertex_global += ucode; r_gen_code.fragment_global += ucode; - r_gen_code.texture_uniforms[E->get().texture_order] = _mkid(E->key()); - r_gen_code.texture_hints[E->get().texture_order] = E->get().hint; + r_gen_code.texture_uniforms.write[E->get().texture_order] = _mkid(E->key()); + r_gen_code.texture_hints.write[E->get().texture_order] = E->get().hint; + r_gen_code.texture_types.write[E->get().texture_order] = E->get().type; } else { if (!uses_uniforms) { r_gen_code.defines.push_back(String("#define USE_MATERIAL\n").ascii()); uses_uniforms = true; } - uniform_defines[E->get().order] = ucode; - uniform_sizes[E->get().order] = _get_datatype_size(E->get().type); - uniform_alignments[E->get().order] = _get_datatype_alignment(E->get().type); + uniform_defines.write[E->get().order] = ucode; + uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type); + uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type); } p_actions.uniforms->insert(E->key(), E->get()); @@ -700,6 +702,11 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener } } else if (cfnode->flow_op == SL::FLOW_OP_DISCARD) { + if (p_actions.usage_flag_pointers.has("DISCARD") && !used_flag_pointers.has("DISCARD")) { + *p_actions.usage_flag_pointers["DISCARD"] = true; + used_flag_pointers.insert("DISCARD"); + } + code = "discard;"; } else if (cfnode->flow_op == SL::FLOW_OP_CONTINUE) { diff --git a/drivers/gles3/shader_compiler_gles3.h b/drivers/gles3/shader_compiler_gles3.h index bf776ee062..7a32057741 100644 --- a/drivers/gles3/shader_compiler_gles3.h +++ b/drivers/gles3/shader_compiler_gles3.h @@ -52,6 +52,7 @@ public: Vector<CharString> defines; Vector<StringName> texture_uniforms; + Vector<ShaderLanguage::DataType> texture_types; Vector<ShaderLanguage::ShaderNode::Uniform::Hint> texture_hints; Vector<uint32_t> uniform_offsets; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 08b8a1cc26..ca0ce5cd3e 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -554,7 +554,7 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() { v.texture_uniform_locations.resize(cc->texture_uniforms.size()); for (int i = 0; i < cc->texture_uniforms.size(); i++) { - v.texture_uniform_locations[i] = glGetUniformLocation(v.id, String(cc->texture_uniforms[i]).ascii().get_data()); + v.texture_uniform_locations.write[i] = glGetUniformLocation(v.id, String(cc->texture_uniforms[i]).ascii().get_data()); glUniform1i(v.texture_uniform_locations[i], i + base_material_tex_index); } } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index abb236138f..dee8bcbc58 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -91,6 +91,7 @@ layout(std140) uniform SceneData { //ubo:0 mediump float subsurface_scatter_width; mediump float ambient_occlusion_affect_light; mediump float ambient_occlusion_affect_ao_channel; + mediump float opaque_prepass_threshold; bool fog_depth_enabled; highp float fog_depth_begin; @@ -338,7 +339,7 @@ void main() { #endif #endif - float roughness=0.0; + float roughness = 1.0; //defines that make writing custom shaders easier #define projection_matrix local_projection @@ -679,6 +680,7 @@ layout(std140) uniform SceneData { mediump float subsurface_scatter_width; mediump float ambient_occlusion_affect_light; mediump float ambient_occlusion_affect_ao_channel; + mediump float opaque_prepass_threshold; bool fog_depth_enabled; highp float fog_depth_begin; @@ -1026,12 +1028,11 @@ LIGHT_SHADER_CODE diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif -#if defined(TRANSMISSION_USED) - diffuse_light += light_color * diffuse_color * mix(vec3(diffuse_brdf_NL), vec3(M_PI), transmission) * attenuation; -#else diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; -#endif +#if defined(TRANSMISSION_USED) + diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation; +#endif #if defined(LIGHT_USE_RIM) @@ -1607,18 +1608,18 @@ void main() { //lay out everything, whathever is unused is optimized away anyway highp vec3 vertex = vertex_interp; - vec3 albedo = vec3(0.8,0.8,0.8); + vec3 albedo = vec3(1.0); vec3 transmission = vec3(0.0); float metallic = 0.0; float specular = 0.5; - vec3 emission = vec3(0.0,0.0,0.0); + vec3 emission = vec3(0.0); float roughness = 1.0; float rim = 0.0; float rim_tint = 0.0; - float clearcoat=0.0; - float clearcoat_gloss=0.0; - float anisotropy = 1.0; - vec2 anisotropy_flow = vec2(1.0,0.0); + float clearcoat = 0.0; + float clearcoat_gloss = 0.0; + float anisotropy = 0.0; + vec2 anisotropy_flow = vec2(1.0, 0.0); #if defined(ENABLE_AO) float ao=1.0; @@ -1628,7 +1629,7 @@ void main() { float alpha = 1.0; #if defined(DO_SIDE_CHECK) - float side=float(gl_FrontFacing)*2.0-1.0; + float side=gl_FrontFacing ? 1.0 : -1.0; #else float side=1.0; #endif @@ -1690,9 +1691,10 @@ FRAGMENT_SHADER_CODE #ifdef USE_OPAQUE_PREPASS - if (alpha<0.99) { + if (alpha<opaque_prepass_threshold) { discard; } + #endif #if defined(ENABLE_NORMALMAP) @@ -2138,6 +2140,8 @@ FRAGMENT_SHADER_CODE #else + + //approximate ambient scale for SSAO, since we will lack full ambient float max_emission=max(emission.r,max(emission.g,emission.b)); float max_ambient=max(ambient_light.r,max(ambient_light.g,ambient_light.b)); @@ -2169,7 +2173,6 @@ FRAGMENT_SHADER_CODE frag_color=vec4(emission+ambient_light+diffuse_light+specular_light,alpha); #endif //SHADELESS - #endif //USE_MULTIPLE_RENDER_TARGETS diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 3622e48ecd..d1104eec18 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -64,18 +64,32 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l ad->pa_status++; } +void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + ad->pa_rec_map = l->channel_map; + ad->pa_status++; +} + void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + ad->capture_default_device = i->default_source_name; ad->default_device = i->default_sink_name; ad->pa_status++; } -void AudioDriverPulseAudio::detect_channels() { +void AudioDriverPulseAudio::detect_channels(bool capture) { - pa_channel_map_init_stereo(&pa_map); + pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); - if (device_name == "Default") { + String device = capture ? capture_device_name : device_name; + if (device == "Default") { // Get the default output device name pa_status = 0; pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); @@ -93,16 +107,22 @@ void AudioDriverPulseAudio::detect_channels() { } } - char device[1024]; - if (device_name == "Default") { - strcpy(device, default_device.utf8().get_data()); + char dev[1024]; + if (device == "Default") { + strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); } else { - strcpy(device, device_name.utf8().get_data()); + strcpy(dev, device.utf8().get_data()); } // Now using the device name get the amount of channels pa_status = 0; - pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); + pa_operation *pa_op; + if (capture) { + pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); + } else { + pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); + } + if (pa_op) { while (pa_status == 0) { int ret = pa_mainloop_iterate(pa_ml, 1, NULL); @@ -113,7 +133,11 @@ void AudioDriverPulseAudio::detect_channels() { pa_operation_unref(pa_op); } else { - ERR_PRINT("pa_context_get_sink_info_by_name error"); + if (capture) { + ERR_PRINT("pa_context_get_source_info_by_name error"); + } else { + ERR_PRINT("pa_context_get_sink_info_by_name error"); + } } } @@ -195,6 +219,10 @@ Error AudioDriverPulseAudio::init_device() { samples_in.resize(buffer_frames * channels); samples_out.resize(pa_buffer_size); + // Reset audio input to keep synchronisation. + input_position = 0; + input_size = 0; + return OK; } @@ -287,74 +315,71 @@ float AudioDriverPulseAudio::get_latency() { void AudioDriverPulseAudio::thread_func(void *p_udata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata; + unsigned int write_ofs = 0; + size_t avail_bytes = 0; while (!ad->exit_thread) { - ad->lock(); - ad->start_counting_ticks(); - - if (!ad->active) { - for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out[i] = 0; - } + size_t read_bytes = 0; + size_t written_bytes = 0; - } else { - ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + if (avail_bytes == 0) { + ad->lock(); + ad->start_counting_ticks(); - if (ad->channels == ad->pa_map.channels) { + if (!ad->active) { for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { - ad->samples_out[i] = ad->samples_in[i] >> 16; + ad->samples_out.write[i] = 0; } } else { - // Uneven amount of channels - unsigned int in_idx = 0; - unsigned int out_idx = 0; + ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); - for (unsigned int i = 0; i < ad->buffer_frames; i++) { - for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { - ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16; + if (ad->channels == ad->pa_map.channels) { + for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { + ad->samples_out.write[i] = ad->samples_in[i] >> 16; + } + } else { + // Uneven amount of channels + unsigned int in_idx = 0; + unsigned int out_idx = 0; + + for (unsigned int i = 0; i < ad->buffer_frames; i++) { + for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { + ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16; + } + uint32_t l = ad->samples_in[in_idx++]; + uint32_t r = ad->samples_in[in_idx++]; + ad->samples_out.write[out_idx++] = ((l >> 1) + (r >> 1)) >> 16; } - uint32_t l = ad->samples_in[in_idx++]; - uint32_t r = ad->samples_in[in_idx++]; - ad->samples_out[out_idx++] = (l >> 1 + r >> 1) >> 16; } } + + avail_bytes = ad->pa_buffer_size * sizeof(int16_t); + write_ofs = 0; + ad->stop_counting_ticks(); + ad->unlock(); } - int error_code; - int byte_size = ad->pa_buffer_size * sizeof(int16_t); + ad->lock(); + ad->start_counting_ticks(); + int ret; do { ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); } while (ret > 0); - if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { - const void *ptr = ad->samples_out.ptr(); - while (byte_size > 0) { - size_t bytes = pa_stream_writable_size(ad->pa_str); - if (bytes > 0) { - if (bytes > byte_size) { - bytes = byte_size; - } - - ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); - if (ret >= 0) { - byte_size -= bytes; - ptr = (const char *)ptr + bytes; - } + if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { + size_t bytes = pa_stream_writable_size(ad->pa_str); + if (bytes > 0) { + size_t bytes_to_write = MIN(bytes, avail_bytes); + const void *ptr = ad->samples_out.ptr(); + ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, NULL, 0LL, PA_SEEK_RELATIVE); + if (ret != 0) { + ERR_PRINT("pa_stream_write error"); } else { - ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); - if (ret == 0) { - // If pa_mainloop_iterate returns 0 sleep for 1 msec to wait - // for the stream to be able to process more bytes - ad->stop_counting_ticks(); - ad->unlock(); - - OS::get_singleton()->delay_usec(1000); - - ad->lock(); - ad->start_counting_ticks(); - } + avail_bytes -= bytes_to_write; + write_ofs += bytes_to_write; + written_bytes += bytes_to_write; } } } @@ -379,8 +404,64 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } + if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) { + size_t bytes = pa_stream_readable_size(ad->pa_rec_str); + if (bytes > 0) { + const void *ptr = NULL; + size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t); + + bytes = MIN(bytes, maxbytes); + ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes); + if (ret != 0) { + ERR_PRINT("pa_stream_peek error"); + } else { + int16_t *srcptr = (int16_t *)ptr; + for (size_t i = bytes >> 1; i > 0; i--) { + int32_t sample = int32_t(*srcptr++) << 16; + ad->input_buffer_write(sample); + + if (ad->pa_rec_map.channels == 1) { + // In case input device is single channel convert it to Stereo + ad->input_buffer_write(sample); + } + } + + read_bytes += bytes; + ret = pa_stream_drop(ad->pa_rec_str); + if (ret != 0) { + ERR_PRINT("pa_stream_drop error"); + } + } + } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->capture_device_name != ad->capture_new_device) { + ad->capture_device_name = ad->capture_new_device; + ad->capture_finish_device(); + + Error err = ad->capture_init_device(); + if (err != OK) { + ERR_PRINT("PulseAudio: capture_init_device error"); + ad->capture_device_name = "Default"; + ad->capture_new_device = "Default"; + + err = ad->capture_init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } + } + ad->stop_counting_ticks(); ad->unlock(); + + // Let the thread rest a while if we haven't read or write anything + if (written_bytes == 0 && read_bytes == 0) { + OS::get_singleton()->delay_usec(1000); + } } ad->thread_exited = true; @@ -403,7 +484,6 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - int ctr = 0; // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -511,11 +591,165 @@ void AudioDriverPulseAudio::finish() { thread = NULL; } +Error AudioDriverPulseAudio::capture_init_device() { + + // If there is a specified device check that it is really present + if (capture_device_name != "Default") { + Array list = capture_get_device_list(); + if (list.find(capture_device_name) == -1) { + capture_device_name = "Default"; + capture_new_device = "Default"; + } + } + + detect_channels(true); + switch (pa_rec_map.channels) { + case 1: // Mono + case 2: // Stereo + break; + + default: + WARN_PRINTS("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels)); + pa_channel_map_init_stereo(&pa_rec_map); + break; + } + + if (OS::get_singleton()->is_stdout_verbose()) { + print_line("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels"); + } + + pa_sample_spec spec; + + spec.format = PA_SAMPLE_S16LE; + spec.channels = pa_rec_map.channels; + spec.rate = mix_rate; + + int latency = 30; + input_buffer_frames = closest_power_of_2(latency * mix_rate / 1000); + int buffer_size = input_buffer_frames * spec.channels; + + pa_buffer_attr attr; + attr.fragsize = buffer_size * sizeof(int16_t); + + pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map); + if (pa_rec_str == NULL) { + ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); + ERR_FAIL_V(ERR_CANT_OPEN); + } + + const char *dev = capture_device_name == "Default" ? NULL : capture_device_name.utf8().get_data(); + pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); + int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags); + if (error_code < 0) { + ERR_PRINTS("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code))); + ERR_FAIL_V(ERR_CANT_OPEN); + } + + input_buffer.resize(input_buffer_frames * 8); + input_position = 0; + input_size = 0; + + return OK; +} + +void AudioDriverPulseAudio::capture_finish_device() { + + if (pa_rec_str) { + int ret = pa_stream_disconnect(pa_rec_str); + if (ret != 0) { + ERR_PRINTS("PulseAudio: pa_stream_disconnect error: " + String(pa_strerror(ret))); + } + pa_stream_unref(pa_rec_str); + pa_rec_str = NULL; + } +} + +Error AudioDriverPulseAudio::capture_start() { + + lock(); + Error err = capture_init_device(); + unlock(); + + return err; +} + +Error AudioDriverPulseAudio::capture_stop() { + lock(); + capture_finish_device(); + unlock(); + + return OK; +} + +void AudioDriverPulseAudio::capture_set_device(const String &p_name) { + + lock(); + capture_new_device = p_name; + unlock(); +} + +void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + if (l->monitor_of_sink == PA_INVALID_INDEX) { + ad->pa_rec_devices.push_back(l->name); + } + + ad->pa_status++; +} + +Array AudioDriverPulseAudio::capture_get_device_list() { + + pa_rec_devices.clear(); + pa_rec_devices.push_back("Default"); + + if (pa_ctx == NULL) { + return pa_rec_devices; + } + + lock(); + + // Get the device list + pa_status = 0; + pa_operation *pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } + + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } + + unlock(); + + return pa_rec_devices; +} + +String AudioDriverPulseAudio::capture_get_device() { + + lock(); + String name = capture_device_name; + unlock(); + + return name; +} + AudioDriverPulseAudio::AudioDriverPulseAudio() { pa_ml = NULL; pa_ctx = NULL; pa_str = NULL; + pa_rec_str = NULL; mutex = NULL; thread = NULL; @@ -529,6 +763,7 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() { mix_rate = 0; buffer_frames = 0; + input_buffer_frames = 0; pa_buffer_size = 0; channels = 0; pa_ready = 0; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index b471f5f9d5..f8358a452b 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -47,22 +47,30 @@ class AudioDriverPulseAudio : public AudioDriver { pa_mainloop *pa_ml; pa_context *pa_ctx; pa_stream *pa_str; + pa_stream *pa_rec_str; pa_channel_map pa_map; + pa_channel_map pa_rec_map; String device_name; String new_device; String default_device; + String capture_device_name; + String capture_new_device; + String capture_default_device; + Vector<int32_t> samples_in; Vector<int16_t> samples_out; unsigned int mix_rate; unsigned int buffer_frames; + unsigned int input_buffer_frames; unsigned int pa_buffer_size; int channels; int pa_ready; int pa_status; Array pa_devices; + Array pa_rec_devices; bool active; bool thread_exited; @@ -72,13 +80,18 @@ class AudioDriverPulseAudio : public AudioDriver { static void pa_state_cb(pa_context *c, void *userdata); static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata); static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); Error init_device(); void finish_device(); - void detect_channels(); + Error capture_init_device(); + void capture_finish_device(); + + void detect_channels(bool capture = false); static void thread_func(void *p_udata); @@ -91,15 +104,24 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); virtual String get_device(); virtual void set_device(String device); + + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); + virtual void lock(); virtual void unlock(); virtual void finish(); virtual float get_latency(); + virtual Error capture_start(); + virtual Error capture_stop(); + AudioDriverPulseAudio(); ~AudioDriverPulseAudio(); }; diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 1d96f9ee7d..a2f619a6ef 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -32,6 +32,8 @@ #include "audio_driver_wasapi.h" +#include <Functiondiscoverykeys_devpkey.h> + #include "os/os.h" #include "project_settings.h" @@ -52,8 +54,22 @@ const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); +const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); + +#define SAFE_RELEASE(memory) \ + if ((memory) != NULL) { \ + (memory)->Release(); \ + (memory) = NULL; \ + } -static bool default_device_changed = false; +#define REFTIMES_PER_SEC 10000000 +#define REFTIMES_PER_MILLISEC 10000 + +#define CAPTURE_BUFFER_CHANNELS 2 + +static StringName capture_device_id; +static bool default_render_device_changed = false; +static bool default_capture_device_changed = false; class CMMNotificationClient : public IMMNotificationClient { LONG _cRef; @@ -109,8 +125,13 @@ public: } HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { - if (flow == eRender && role == eConsole) { - default_device_changed = true; + if (role == eConsole) { + if (flow == eRender) { + default_render_device_changed = true; + } else if (flow == eCapture) { + default_capture_device_changed = true; + capture_device_id = String(pwstrDeviceId); + } } return S_OK; @@ -123,7 +144,7 @@ public: static CMMNotificationClient notif_client; -Error AudioDriverWASAPI::init_device(bool reinit) { +Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; @@ -134,12 +155,12 @@ Error AudioDriverWASAPI::init_device(bool reinit) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - if (device_name == "Default") { - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + if (p_device->device_name == "Default") { + hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } else { IMMDeviceCollection *devices = NULL; - hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = NULL; @@ -165,7 +186,7 @@ Error AudioDriverWASAPI::init_device(bool reinit) { hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); ERR_BREAK(hr != S_OK); - if (device_name == String(propvar.pwszVal)) { + if (p_device->device_name == String(propvar.pwszVal)) { hr = device->GetId(&strId); ERR_BREAK(hr != S_OK); @@ -186,9 +207,10 @@ Error AudioDriverWASAPI::init_device(bool reinit) { } if (device == NULL) { - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } } + if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. @@ -200,11 +222,15 @@ Error AudioDriverWASAPI::init_device(bool reinit) { } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); + SAFE_RELEASE(enumerator) + if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audio_client); + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&p_device->audio_client); + SAFE_RELEASE(device) + if (reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; @@ -213,75 +239,89 @@ Error AudioDriverWASAPI::init_device(bool reinit) { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } - hr = audio_client->GetMixFormat(&pwfex); + hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along - wasapi_channels = pwfex->nChannels; - format_tag = pwfex->wFormatTag; - bits_per_sample = pwfex->wBitsPerSample; + p_device->channels = pwfex->nChannels; + p_device->format_tag = pwfex->wFormatTag; + p_device->bits_per_sample = pwfex->wBitsPerSample; + p_device->frame_size = (p_device->bits_per_sample / 8) * p_device->channels; - switch (wasapi_channels) { - case 2: // Stereo - case 4: // Surround 3.1 - case 6: // Surround 5.1 - case 8: // Surround 7.1 - channels = wasapi_channels; - break; - - default: - WARN_PRINTS("WASAPI: Unsupported number of channels: " + itos(wasapi_channels)); - channels = 2; - break; - } - - if (format_tag == WAVE_FORMAT_EXTENSIBLE) { + if (p_device->format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { - format_tag = WAVE_FORMAT_PCM; + p_device->format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { - format_tag = WAVE_FORMAT_IEEE_FLOAT; + p_device->format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { - if (format_tag != WAVE_FORMAT_PCM && format_tag != WAVE_FORMAT_IEEE_FLOAT) { + if (p_device->format_tag != WAVE_FORMAT_PCM && p_device->format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } - DWORD streamflags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + DWORD streamflags = 0; if (mix_rate != pwfex->nSamplesPerSec) { streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, 0, 0, pwfex, NULL); + hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - event = CreateEvent(NULL, FALSE, FALSE, NULL); - ERR_FAIL_COND_V(event == NULL, ERR_CANT_OPEN); - - hr = audio_client->SetEventHandle(event); + if (p_capture) { + hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); + } else { + hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); + } ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - hr = audio_client->GetService(IID_IAudioRenderClient, (void **)&render_client); - ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + // Free memory + CoTaskMemFree(pwfex); + SAFE_RELEASE(device) + + return OK; +} + +Error AudioDriverWASAPI::init_render_device(bool reinit) { + + Error err = audio_device_init(&audio_output, false, reinit); + if (err != OK) + return err; + + switch (audio_output.channels) { + case 2: // Stereo + case 4: // Surround 3.1 + case 6: // Surround 5.1 + case 8: // Surround 7.1 + channels = audio_output.channels; + break; + + default: + WARN_PRINTS("WASAPI: Unsupported number of channels: " + itos(audio_output.channels)); + channels = 2; + break; + } UINT32 max_frames; - hr = audio_client->GetBufferSize(&max_frames); + HRESULT hr = audio_output.audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size buffer_frames = max_frames; // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) - buffer_size = buffer_frames * channels; - samples_in.resize(buffer_size); + samples_in.resize(buffer_frames * channels); + + input_position = 0; + input_size = 0; if (OS::get_singleton()->is_stdout_verbose()) { print_line("WASAPI: detected " + itos(channels) + " channels"); @@ -291,41 +331,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) { return OK; } -Error AudioDriverWASAPI::finish_device() { +Error AudioDriverWASAPI::init_capture_device(bool reinit) { - if (audio_client) { - if (active) { - audio_client->Stop(); - active = false; - } + Error err = audio_device_init(&audio_input, true, reinit); + if (err != OK) + return err; - audio_client->Release(); - audio_client = NULL; - } + // Get the max frames + UINT32 max_frames; + HRESULT hr = audio_input.audio_client->GetBufferSize(&max_frames); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - if (render_client) { - render_client->Release(); - render_client = NULL; - } + // Set the buffer size + input_buffer.resize(max_frames * CAPTURE_BUFFER_CHANNELS); + input_position = 0; + input_size = 0; - if (audio_client) { - audio_client->Release(); - audio_client = NULL; + return OK; +} + +Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { + + if (p_device->active) { + if (p_device->audio_client) { + p_device->audio_client->Stop(); + } + + p_device->active = false; } + SAFE_RELEASE(p_device->audio_client) + SAFE_RELEASE(p_device->render_client) + SAFE_RELEASE(p_device->capture_client) + return OK; } +Error AudioDriverWASAPI::finish_render_device() { + + return audio_device_finish(&audio_output); +} + +Error AudioDriverWASAPI::finish_capture_device() { + + return audio_device_finish(&audio_input); +} + Error AudioDriverWASAPI::init() { mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); - Error err = init_device(); + Error err = init_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_device error"); + ERR_PRINT("WASAPI: init_render_device error"); } - active = false; exit_thread = false; thread_exited = false; @@ -345,7 +405,7 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } -Array AudioDriverWASAPI::get_device_list() { +Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { Array list; IMMDeviceCollection *devices = NULL; @@ -358,7 +418,7 @@ Array AudioDriverWASAPI::get_device_list() { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, Array()); - hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, Array()); UINT count = 0; @@ -393,21 +453,63 @@ Array AudioDriverWASAPI::get_device_list() { return list; } +Array AudioDriverWASAPI::get_device_list() { + + return audio_device_get_list(false); +} + String AudioDriverWASAPI::get_device() { - return device_name; + lock(); + String name = audio_output.device_name; + unlock(); + + return name; } void AudioDriverWASAPI::set_device(String device) { lock(); - new_device = device; + audio_output.new_device = device; unlock(); } -void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) { - if (ad->format_tag == WAVE_FORMAT_PCM) { - switch (ad->bits_per_sample) { +int32_t AudioDriverWASAPI::read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i) { + if (format_tag == WAVE_FORMAT_PCM) { + int32_t sample = 0; + switch (bits_per_sample) { + case 8: + sample = int32_t(((int8_t *)buffer)[i]) << 24; + break; + + case 16: + sample = int32_t(((int16_t *)buffer)[i]) << 16; + break; + + case 24: + sample |= int32_t(((int8_t *)buffer)[i * 3 + 2]) << 24; + sample |= int32_t(((int8_t *)buffer)[i * 3 + 1]) << 16; + sample |= int32_t(((int8_t *)buffer)[i * 3 + 0]) << 8; + break; + + case 32: + sample = ((int32_t *)buffer)[i]; + break; + } + + return sample; + } else if (format_tag == WAVE_FORMAT_IEEE_FLOAT) { + return int32_t(((float *)buffer)[i] * 32768.0) << 16; + } else { + ERR_PRINT("WASAPI: Unknown format tag"); + } + + return 0; +} + +void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample) { + if (format_tag == WAVE_FORMAT_PCM) { + switch (bits_per_sample) { case 8: ((int8_t *)buffer)[i] = sample >> 24; break; @@ -426,83 +528,99 @@ void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, ((int32_t *)buffer)[i] = sample; break; } - } else if (ad->format_tag == WAVE_FORMAT_IEEE_FLOAT) { + } else if (format_tag == WAVE_FORMAT_IEEE_FLOAT) { ((float *)buffer)[i] = (sample >> 16) / 32768.f; } else { ERR_PRINT("WASAPI: Unknown format tag"); - ad->exit_thread = true; } } void AudioDriverWASAPI::thread_func(void *p_udata) { AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata; + uint32_t avail_frames = 0; + uint32_t write_ofs = 0; while (!ad->exit_thread) { - ad->lock(); - ad->start_counting_ticks(); + uint32_t read_frames = 0; + uint32_t written_frames = 0; - if (ad->active) { - ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); - } else { - for (unsigned int i = 0; i < ad->buffer_size; i++) { - ad->samples_in[i] = 0; + if (avail_frames == 0) { + ad->lock(); + ad->start_counting_ticks(); + + if (ad->audio_output.active) { + ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); + } else { + for (unsigned int i = 0; i < ad->samples_in.size(); i++) { + ad->samples_in.write[i] = 0; + } } - } - ad->stop_counting_ticks(); - ad->unlock(); + avail_frames = ad->buffer_frames; + write_ofs = 0; - unsigned int left_frames = ad->buffer_frames; - unsigned int buffer_idx = 0; - while (left_frames > 0 && ad->audio_client) { - WaitForSingleObject(ad->event, 1000); + ad->stop_counting_ticks(); + ad->unlock(); + } - ad->lock(); - ad->start_counting_ticks(); + ad->lock(); + ad->start_counting_ticks(); + + if (avail_frames > 0 && ad->audio_output.audio_client) { UINT32 cur_frames; bool invalidated = false; - HRESULT hr = ad->audio_client->GetCurrentPadding(&cur_frames); + HRESULT hr = ad->audio_output.audio_client->GetCurrentPadding(&cur_frames); if (hr == S_OK) { - // Check how much frames are available on the WASAPI buffer - UINT32 avail_frames = ad->buffer_frames - cur_frames; - UINT32 write_frames = avail_frames > left_frames ? left_frames : avail_frames; - BYTE *buffer = NULL; - hr = ad->render_client->GetBuffer(write_frames, &buffer); - if (hr == S_OK) { - // We're using WASAPI Shared Mode so we must convert the buffer - - if (ad->channels == ad->wasapi_channels) { - for (unsigned int i = 0; i < write_frames * ad->channels; i++) { - ad->write_sample(ad, buffer, i, ad->samples_in[buffer_idx++]); - } - } else { - for (unsigned int i = 0; i < write_frames; i++) { - for (unsigned int j = 0; j < MIN(ad->channels, ad->wasapi_channels); j++) { - ad->write_sample(ad, buffer, i * ad->wasapi_channels + j, ad->samples_in[buffer_idx++]); + // Check how much frames are available on the WASAPI buffer + UINT32 write_frames = MIN(ad->buffer_frames - cur_frames, avail_frames); + if (write_frames > 0) { + BYTE *buffer = NULL; + hr = ad->audio_output.render_client->GetBuffer(write_frames, &buffer); + if (hr == S_OK) { + + // We're using WASAPI Shared Mode so we must convert the buffer + if (ad->channels == ad->audio_output.channels) { + for (unsigned int i = 0; i < write_frames * ad->channels; i++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i, ad->samples_in.write[write_ofs++]); } - if (ad->wasapi_channels > ad->channels) { - for (unsigned int j = ad->channels; j < ad->wasapi_channels; j++) { - ad->write_sample(ad, buffer, i * ad->wasapi_channels + j, 0); + } else { + for (unsigned int i = 0; i < write_frames; i++) { + for (unsigned int j = 0; j < MIN(ad->channels, ad->audio_output.channels); j++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, ad->samples_in.write[write_ofs++]); + } + if (ad->audio_output.channels > ad->channels) { + for (unsigned int j = ad->channels; j < ad->audio_output.channels; j++) { + ad->write_sample(ad->audio_output.format_tag, ad->audio_output.bits_per_sample, buffer, i * ad->audio_output.channels + j, 0); + } } } } - } - hr = ad->render_client->ReleaseBuffer(write_frames, 0); - if (hr != S_OK) { - ERR_PRINT("WASAPI: Release buffer error"); - } + hr = ad->audio_output.render_client->ReleaseBuffer(write_frames, 0); + if (hr != S_OK) { + ERR_PRINT("WASAPI: Release buffer error"); + } - left_frames -= write_frames; - } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - invalidated = true; - } else { - ERR_PRINT("WASAPI: Get buffer error"); - ad->exit_thread = true; + avail_frames -= write_frames; + written_frames += write_frames; + } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { + // Device is not valid anymore, reopen it + + Error err = ad->finish_render_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_render_device error"); + } else { + // We reopened the device and samples_in may have resized, so invalidate the current avail_frames + avail_frames = 0; + } + } else { + ERR_PRINT("WASAPI: Get buffer error"); + ad->exit_thread = true; + } } } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { invalidated = true; @@ -514,47 +632,117 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { // Device is not valid anymore WARN_PRINT("WASAPI: Current device invalidated, closing device"); - Error err = ad->finish_device(); + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + ERR_PRINT("WASAPI: finish_render_device error"); } } - - ad->stop_counting_ticks(); - ad->unlock(); } - ad->lock(); - ad->start_counting_ticks(); - // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->device_name == "Default" && default_device_changed) { - Error err = ad->finish_device(); + if (ad->audio_output.device_name == "Default" && default_render_device_changed) { + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + ERR_PRINT("WASAPI: finish_render_device error"); } - default_device_changed = false; + default_render_device_changed = false; } // User selected a new device, finish the current one so we'll init the new device - if (ad->device_name != ad->new_device) { - ad->device_name = ad->new_device; - Error err = ad->finish_device(); + if (ad->audio_output.device_name != ad->audio_output.new_device) { + ad->audio_output.device_name = ad->audio_output.new_device; + Error err = ad->finish_render_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_device error"); + ERR_PRINT("WASAPI: finish_render_device error"); } } - if (!ad->audio_client) { - Error err = ad->init_device(true); + if (!ad->audio_output.audio_client) { + Error err = ad->init_render_device(true); if (err == OK) { ad->start(); } } + if (ad->audio_input.active) { + UINT32 packet_length = 0; + BYTE *data; + UINT32 num_frames_available; + DWORD flags; + + HRESULT hr = ad->audio_input.capture_client->GetNextPacketSize(&packet_length); + if (hr == S_OK) { + while (packet_length != 0) { + hr = ad->audio_input.capture_client->GetBuffer(&data, &num_frames_available, &flags, NULL, NULL); + ERR_BREAK(hr != S_OK); + + // fixme: Only works for floating point atm + for (int j = 0; j < num_frames_available; j++) { + int32_t l, r; + + if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { + l = r = 0; + } else { + if (ad->audio_input.channels == 2) { + l = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j * 2); + r = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j * 2 + 1); + } else if (ad->audio_input.channels == 1) { + l = r = read_sample(ad->audio_input.format_tag, ad->audio_input.bits_per_sample, data, j); + } else { + l = r = 0; + ERR_PRINT("WASAPI: unsupported channel count in microphone!"); + } + } + + ad->input_buffer_write(l); + ad->input_buffer_write(r); + } + + read_frames += num_frames_available; + + hr = ad->audio_input.capture_client->ReleaseBuffer(num_frames_available); + ERR_BREAK(hr != S_OK); + + hr = ad->audio_input.capture_client->GetNextPacketSize(&packet_length); + ERR_BREAK(hr != S_OK); + } + } + + // If we're using the Default device and it changed finish it so we'll re-init the device + if (ad->audio_input.device_name == "Default" && default_capture_device_changed) { + Error err = ad->finish_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_capture_device error"); + } + + default_capture_device_changed = false; + } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->audio_input.device_name != ad->audio_input.new_device) { + ad->audio_input.device_name = ad->audio_input.new_device; + Error err = ad->finish_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_capture_device error"); + } + } + + if (!ad->audio_input.audio_client) { + Error err = ad->init_capture_device(true); + if (err == OK) { + ad->capture_start(); + } + } + } + ad->stop_counting_ticks(); ad->unlock(); + + // Let the thread rest a while if we haven't read or write anything + if (written_frames == 0 && read_frames == 0) { + OS::get_singleton()->delay_usec(1000); + } } ad->thread_exited = true; @@ -562,12 +750,12 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { void AudioDriverWASAPI::start() { - if (audio_client) { - HRESULT hr = audio_client->Start(); + if (audio_output.audio_client) { + HRESULT hr = audio_output.audio_client->Start(); if (hr != S_OK) { ERR_PRINT("WASAPI: Start failed"); } else { - active = true; + audio_output.active = true; } } } @@ -594,7 +782,8 @@ void AudioDriverWASAPI::finish() { thread = NULL; } - finish_device(); + finish_capture_device(); + finish_render_device(); if (mutex) { memdelete(mutex); @@ -602,30 +791,70 @@ void AudioDriverWASAPI::finish() { } } +Error AudioDriverWASAPI::capture_start() { + + Error err = init_capture_device(); + if (err != OK) { + ERR_PRINT("WASAPI: init_capture_device error"); + return err; + } + + if (audio_input.active == false) { + audio_input.audio_client->Start(); + audio_input.active = true; + + return OK; + } + + return FAILED; +} + +Error AudioDriverWASAPI::capture_stop() { + + if (audio_input.active == true) { + audio_input.audio_client->Stop(); + audio_input.active = false; + + return OK; + } + + return FAILED; +} + +void AudioDriverWASAPI::capture_set_device(const String &p_name) { + + lock(); + audio_input.new_device = p_name; + unlock(); +} + +Array AudioDriverWASAPI::capture_get_device_list() { + + return audio_device_get_list(true); +} + +String AudioDriverWASAPI::capture_get_device() { + + lock(); + String name = audio_input.device_name; + unlock(); + + return name; +} + AudioDriverWASAPI::AudioDriverWASAPI() { - audio_client = NULL; - render_client = NULL; mutex = NULL; thread = NULL; - format_tag = 0; - bits_per_sample = 0; - samples_in.clear(); - buffer_size = 0; channels = 0; - wasapi_channels = 0; mix_rate = 0; buffer_frames = 0; thread_exited = false; exit_thread = false; - active = false; - - device_name = "Default"; - new_device = "Default"; } #endif diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index f3ee5976eb..3d94f3ba49 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -43,35 +43,63 @@ class AudioDriverWASAPI : public AudioDriver { - HANDLE event; - IAudioClient *audio_client; - IAudioRenderClient *render_client; + class AudioDeviceWASAPI { + public: + IAudioClient *audio_client; + IAudioRenderClient *render_client; + IAudioCaptureClient *capture_client; + bool active; + + WORD format_tag; + WORD bits_per_sample; + unsigned int channels; + unsigned int frame_size; + + String device_name; + String new_device; + + AudioDeviceWASAPI() { + audio_client = NULL; + render_client = NULL; + capture_client = NULL; + active = false; + format_tag = 0; + bits_per_sample = 0; + channels = 0; + frame_size = 0; + device_name = "Default"; + new_device = "Default"; + } + }; + + AudioDeviceWASAPI audio_input; + AudioDeviceWASAPI audio_output; + Mutex *mutex; Thread *thread; - String device_name; - String new_device; - - WORD format_tag; - WORD bits_per_sample; - Vector<int32_t> samples_in; - unsigned int buffer_size; unsigned int channels; - unsigned int wasapi_channels; int mix_rate; int buffer_frames; bool thread_exited; mutable bool exit_thread; - bool active; - _FORCE_INLINE_ void write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample); + static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample); + static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); - Error init_device(bool reinit = false); - Error finish_device(); + Error init_render_device(bool reinit = false); + Error init_capture_device(bool reinit = false); + + Error finish_render_device(); + Error finish_capture_device(); + + Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit); + Error audio_device_finish(AudioDeviceWASAPI *p_device); + Array audio_device_get_list(bool p_capture); public: virtual const char *get_name() const { @@ -89,6 +117,12 @@ public: virtual void unlock(); virtual void finish(); + virtual Error capture_start(); + virtual Error capture_stop(); + virtual Array capture_get_device_list(); + virtual void capture_set_device(const String &p_name); + virtual String capture_get_device(); + AudioDriverWASAPI(); }; diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/drivers/winmidi/SCsub @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +Import('env') + +# Driver source files +env.add_source_files(env.drivers_sources, "*.cpp") + +Export('env') diff --git a/drivers/winmidi/win_midi.cpp b/drivers/winmidi/win_midi.cpp new file mode 100644 index 0000000000..6da6e31b2b --- /dev/null +++ b/drivers/winmidi/win_midi.cpp @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* win_midi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#include "win_midi.h" +#include "print_string.h" + +void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { + + if (wMsg == MIM_DATA) { + receive_input_packet((uint64_t)dwParam2, (uint8_t *)&dwParam1, 3); + } +} + +Error MIDIDriverWinMidi::open() { + + for (UINT i = 0; i < midiInGetNumDevs(); i++) { + HMIDIIN midi_in; + + MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)this, CALLBACK_FUNCTION); + if (res == MMSYSERR_NOERROR) { + midiInStart(midi_in); + connected_sources.insert(i, midi_in); + } else { + char err[256]; + midiInGetErrorText(res, err, 256); + ERR_PRINTS("midiInOpen error: " + String(err)); + } + } + + return OK; +} + +PoolStringArray MIDIDriverWinMidi::get_connected_inputs() { + + PoolStringArray list; + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + UINT id = 0; + MMRESULT res = midiInGetID(midi_in, &id); + if (res == MMSYSERR_NOERROR) { + MIDIINCAPS caps; + res = midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS)); + if (res == MMSYSERR_NOERROR) { + list.push_back(caps.szPname); + } + } + } + + return list; +} + +void MIDIDriverWinMidi::close() { + + for (int i = 0; i < connected_sources.size(); i++) { + HMIDIIN midi_in = connected_sources[i]; + midiInStop(midi_in); + midiInClose(midi_in); + } + connected_sources.clear(); +} + +MIDIDriverWinMidi::MIDIDriverWinMidi() { +} + +MIDIDriverWinMidi::~MIDIDriverWinMidi() { + + close(); +} + +#endif diff --git a/drivers/winmidi/win_midi.h b/drivers/winmidi/win_midi.h new file mode 100644 index 0000000000..1cf9b19b5d --- /dev/null +++ b/drivers/winmidi/win_midi.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* win_midi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WINMIDI_ENABLED + +#ifndef WIN_MIDI_H +#define WIN_MIDI_H + +#include <stdio.h> +#include <windows.h> + +#include <mmsystem.h> + +#include "core/vector.h" +#include "os/midi_driver.h" + +class MIDIDriverWinMidi : public MIDIDriver { + + Vector<HMIDIIN> connected_sources; + + static void CALLBACK read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); + +public: + virtual Error open(); + virtual void close(); + + virtual PoolStringArray get_connected_inputs(); + + MIDIDriverWinMidi(); + virtual ~MIDIDriverWinMidi(); +}; + +#endif +#endif diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index db108f9c6b..a1002ef4f9 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -97,8 +97,6 @@ void AudioDriverXAudio2::thread_func(void *p_udata) { AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata; - uint64_t usdelay = (ad->buffer_size / float(ad->mix_rate)) * 1000000; - while (!ad->exit_thread) { if (!ad->active) { diff --git a/editor/SCsub b/editor/SCsub index a9343f7f36..4fa287c33b 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -5,149 +5,14 @@ env.editor_sources = [] import os import os.path -from compat import encode_utf8, byte_to_str, open_utf8, escape_string - -def make_certs_header(target, source, env): - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - f = open(src, "rb") - g = open_utf8(dst, "w") - buf = f.read() - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _CERTS_RAW_H\n") - g.write("#define _CERTS_RAW_H\n") - g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") - g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") - g.write("static const unsigned char _certs_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") - - g.close() - f.close() - - -def make_doc_header(target, source, env): - - dst = target[0].srcnode().abspath - g = open_utf8(dst, "w") - buf = "" - docbegin = "" - docend = "" - for s in source: - src = s.srcnode().abspath - if not src.endswith(".xml"): - continue - with open_utf8(src, "r") as f: - content = f.read() - buf += content - - buf = encode_utf8(docbegin + buf + docend) - decomp_size = len(buf) - import zlib - buf = zlib.compress(buf) - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _DOC_DATA_RAW_H\n") - g.write("#define _DOC_DATA_RAW_H\n") - g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n") - g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n") - g.write("static const unsigned char _doc_data_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + byte_to_str(buf[i]) + ",\n") - g.write("};\n") - - g.write("#endif") - - g.close() - - -def make_fonts_header(target, source, env): - - dst = target[0].srcnode().abspath - - g = open_utf8(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _EDITOR_FONTS_H\n") - g.write("#define _EDITOR_FONTS_H\n") - - # saving uncompressed, since freetype will reference from memory pointer - xl_names = [] - for i in range(len(source)): - with open(source[i].srcnode().abspath, "rb")as f: - buf = f.read() - - name = os.path.splitext(os.path.basename(source[i].srcnode().abspath))[0] - - g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n") - g.write("static const unsigned char _font_" + name + "[] = {\n") - for i in range(len(buf)): - g.write("\t" + byte_to_str(buf[i]) + ",\n") - - g.write("};\n") - - g.write("#endif") - - g.close() - +from platform_methods import run_in_subprocess +from compat import open_utf8 +import editor_builders -def make_translations_header(target, source, env): - - dst = target[0].srcnode().abspath - - g = open_utf8(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _EDITOR_TRANSLATIONS_H\n") - g.write("#define _EDITOR_TRANSLATIONS_H\n") - - import zlib - import os.path - - paths = [node.srcnode().abspath for node in source] - sorted_paths = sorted(paths, key=lambda path: os.path.splitext(os.path.basename(path))[0]) - - xl_names = [] - for i in range(len(sorted_paths)): - with open(sorted_paths[i], "rb") as f: - buf = f.read() - decomp_size = len(buf) - buf = zlib.compress(buf) - name = os.path.splitext(os.path.basename(sorted_paths[i]))[0] - - g.write("static const unsigned char _translation_" + name + "_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + byte_to_str(buf[i]) + ",\n") - - g.write("};\n") - - xl_names.append([name, len(buf), str(decomp_size)]) - - g.write("struct EditorTranslationList {\n") - g.write("\tconst char* lang;\n") - g.write("\tint comp_size;\n") - g.write("\tint uncomp_size;\n") - g.write("\tconst unsigned char* data;\n") - g.write("};\n\n") - g.write("static EditorTranslationList _editor_translations[] = {\n") - for x in xl_names: - g.write("\t{ \"" + x[0] + "\", " + str(x[1]) + ", " + str(x[2]) + ", _translation_" + x[0] + "_compressed},\n") - g.write("\t{NULL, 0, 0, NULL}\n") - g.write("};\n") - - g.write("#endif") - - g.close() def _make_doc_data_class_path(to_path): - g = open_utf8(os.path.join(to_path,"doc_data_class_path.gen.h"), "w") + # NOTE: It is safe to generate this file here, since this is still executed serially + g = open_utf8(os.path.join(to_path, "doc_data_class_path.gen.h"), "w") g.write("static const int _doc_data_class_path_count = " + str(len(env.doc_class_path)) + ";\n") g.write("struct _DocDataClassPath { const char* name; const char* path; };\n") @@ -169,6 +34,8 @@ if env['tools']: reg_exporters += '\tregister_' + e + '_exporter();\n' reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n' reg_exporters += '}\n' + + # NOTE: It is safe to generate this file here, since this is still executed serially with open_utf8("register_exporters.gen.cpp", "w") as f: f.write(reg_exporters_inc) f.write(reg_exporters) @@ -192,24 +59,38 @@ if env['tools']: docs = sorted(docs) env.Depends("#editor/doc_data_compressed.gen.h", docs) - env.CommandNoCache("#editor/doc_data_compressed.gen.h", docs, make_doc_header) + env.CommandNoCache("#editor/doc_data_compressed.gen.h", docs, run_in_subprocess(editor_builders.make_doc_header)) + # Certificates env.Depends("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt") - env.CommandNoCache("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", make_certs_header) + env.CommandNoCache("#editor/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(editor_builders.make_certs_header)) import glob + path = env.Dir('.').abspath # Translations tlist = glob.glob(path + "/translations/*.po") env.Depends('#editor/translations.gen.h', tlist) - env.CommandNoCache('#editor/translations.gen.h', tlist, make_translations_header) + env.CommandNoCache('#editor/translations.gen.h', tlist, run_in_subprocess(editor_builders.make_translations_header)) # Fonts flist = glob.glob(path + "/../thirdparty/fonts/*.ttf") flist.append(glob.glob(path + "/../thirdparty/fonts/*.otf")) env.Depends('#editor/builtin_fonts.gen.h', flist) - env.CommandNoCache('#editor/builtin_fonts.gen.h', flist, make_fonts_header) + env.CommandNoCache('#editor/builtin_fonts.gen.h', flist, run_in_subprocess(editor_builders.make_fonts_header)) + + # Authors + env.Depends('#editor/authors.gen.h', "../AUTHORS.md") + env.CommandNoCache('#editor/authors.gen.h', "../AUTHORS.md", run_in_subprocess(editor_builders.make_authors_header)) + + # Donors + env.Depends('#editor/donors.gen.h', "../DONORS.md") + env.CommandNoCache('#editor/donors.gen.h', "../DONORS.md", run_in_subprocess(editor_builders.make_donors_header)) + + # License + env.Depends('#editor/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]) + env.CommandNoCache('#editor/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(editor_builders.make_license_header)) env.add_source_files(env.editor_sources, "*.cpp") env.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"]) diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 42d5ea120e..4c4830ad7a 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -231,10 +231,10 @@ public: if (Variant::can_convert(args[idx].get_type(), t)) { Variant old = args[idx]; Variant *ptrs[1] = { &old }; - args[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err); + args.write[idx] = Variant::construct(t, (const Variant **)ptrs, 1, err); } else { - args[idx] = Variant::construct(t, NULL, 0, err); + args.write[idx] = Variant::construct(t, NULL, 0, err); } change_notify_deserved = true; d_new["args"] = args; @@ -248,7 +248,7 @@ public: _fix_node_path(value); } - args[idx] = value; + args.write[idx] = value; d_new["args"] = args; mergeable = true; } @@ -3014,12 +3014,12 @@ PropertyInfo AnimationTrackEditor::_find_hint_for_track(int p_idx, NodePath &r_b if (res.is_valid()) { property_info_base = res; if (r_current_val) { - *r_current_val = res->get(leftover_path[leftover_path.size() - 1]); + *r_current_val = res->get_indexed(leftover_path); } } else if (node) { property_info_base = node; if (r_current_val) { - *r_current_val = node->get(leftover_path[leftover_path.size() - 1]); + *r_current_val = node->get_indexed(leftover_path); } } @@ -3053,31 +3053,31 @@ static Vector<String> _get_bezier_subindices_for_type(Variant::Type p_type, bool subindices.push_back(""); } break; case Variant::VECTOR2: { - subindices.push_back(".x"); - subindices.push_back(".y"); + subindices.push_back(":x"); + subindices.push_back(":y"); } break; case Variant::VECTOR3: { - subindices.push_back(".x"); - subindices.push_back(".y"); - subindices.push_back(".z"); + subindices.push_back(":x"); + subindices.push_back(":y"); + subindices.push_back(":z"); } break; case Variant::QUAT: { - subindices.push_back(".x"); - subindices.push_back(".y"); - subindices.push_back(".z"); - subindices.push_back(".w"); + subindices.push_back(":x"); + subindices.push_back(":y"); + subindices.push_back(":z"); + subindices.push_back(":w"); } break; case Variant::COLOR: { - subindices.push_back(".r"); - subindices.push_back(".g"); - subindices.push_back(".b"); - subindices.push_back(".a"); + subindices.push_back(":r"); + subindices.push_back(":g"); + subindices.push_back(":b"); + subindices.push_back(":a"); } break; case Variant::PLANE: { - subindices.push_back(".x"); - subindices.push_back(".y"); - subindices.push_back(".z"); - subindices.push_back(".d"); + subindices.push_back(":x"); + subindices.push_back(":y"); + subindices.push_back(":z"); + subindices.push_back(":d"); } break; default: { if (r_valid) { @@ -3288,35 +3288,23 @@ void AnimationTrackEditor::_update_tracks() { if (root && root->has_node_and_resource(path)) { RES res; + NodePath base_path; Vector<StringName> leftover_path; Node *node = root->get_node_and_resource(path, res, leftover_path, true); + PropertyInfo pinfo = _find_hint_for_track(i, base_path); Object *object = node; if (res.is_valid()) { object = res.ptr(); - } else { - object = node; } if (object && !leftover_path.empty()) { - //not a property (value track?) - PropertyInfo pinfo; - pinfo.name = leftover_path[leftover_path.size() - 1]; - //now let's see if we can get more info about it - - List<PropertyInfo> plist; - object->get_property_list(&plist); - - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - - if (E->get().name == leftover_path[leftover_path.size() - 1]) { - pinfo = E->get(); - break; - } + if (pinfo.name.empty()) { + pinfo.name = leftover_path[leftover_path.size() - 1]; } for (int j = 0; j < track_edit_plugins.size(); j++) { - track_edit = track_edit_plugins[j]->create_value_track_edit(object, pinfo.type, pinfo.name, pinfo.hint, pinfo.hint_string, pinfo.usage); + track_edit = track_edit_plugins.write[j]->create_value_track_edit(object, pinfo.type, pinfo.name, pinfo.hint, pinfo.hint_string, pinfo.usage); if (track_edit) { break; } @@ -3327,7 +3315,7 @@ void AnimationTrackEditor::_update_tracks() { if (animation->track_get_type(i) == Animation::TYPE_AUDIO) { for (int j = 0; j < track_edit_plugins.size(); j++) { - track_edit = track_edit_plugins[j]->create_audio_track_edit(); + track_edit = track_edit_plugins.write[j]->create_audio_track_edit(); if (track_edit) { break; } @@ -3344,7 +3332,7 @@ void AnimationTrackEditor::_update_tracks() { if (node && Object::cast_to<AnimationPlayer>(node)) { for (int j = 0; j < track_edit_plugins.size(); j++) { - track_edit = track_edit_plugins[j]->create_animation_track_edit(node); + track_edit = track_edit_plugins.write[j]->create_animation_track_edit(node); if (track_edit) { break; } diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index d0c91f10d9..6d444c5422 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -271,8 +271,8 @@ void AnimationTrackEditAudio::draw_key(int p_index, float p_pixels_sec, int p_x, float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; int idx = i - from_x; - lines[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y); - lines[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y); + lines.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y); + lines.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y); } Vector<Color> color; @@ -883,8 +883,8 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; int idx = i - from_x; - lines[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y); - lines[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y); + lines.write[idx * 2 + 0] = Vector2(i, rect.position.y + min * rect.size.y); + lines.write[idx * 2 + 1] = Vector2(i, rect.position.y + max * rect.size.y); } Vector<Color> color; diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp index 6ee4d7f4b0..6ae5ec43a9 100644 --- a/editor/audio_stream_preview.cpp +++ b/editor/audio_stream_preview.cpp @@ -118,8 +118,8 @@ void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) { uint8_t pfrom = CLAMP((min * 0.5 + 0.5) * 255, 0, 255); uint8_t pto = CLAMP((max * 0.5 + 0.5) * 255, 0, 255); - preview->preview->preview[(ofs_write + i) * 2 + 0] = pfrom; - preview->preview->preview[(ofs_write + i) * 2 + 1] = pto; + preview->preview->preview.write[(ofs_write + i) * 2 + 0] = pfrom; + preview->preview->preview.write[(ofs_write + i) * 2 + 1] = pto; } frames_todo -= to_read; diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 6aec6135f1..9d4333bc29 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -725,7 +725,7 @@ void CodeTextEditor::_complete_request() { int i = 0; for (List<String>::Element *E = entries.front(); E; E = E->next()) { - strs[i++] = E->get(); + strs.write[i++] = E->get(); } text_editor->code_complete(strs, forced); @@ -745,7 +745,7 @@ bool CodeTextEditor::_add_font_size(int p_delta) { if (font.is_valid()) { int new_size = CLAMP(font->get_size() + p_delta, 8 * EDSCALE, 96 * EDSCALE); - zoom_nb->set_text(itos(100 * new_size / 14) + "%"); + zoom_nb->set_text(itos(100 * new_size / (14 * EDSCALE)) + "%"); if (new_size != font->get_size()) { EditorSettings::get_singleton()->set("interface/editor/code_font_size", new_size / EDSCALE); @@ -784,6 +784,345 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/open_scripts/v_scroll_speed")); } +void CodeTextEditor::trim_trailing_whitespace() { + bool trimed_whitespace = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + if (line.ends_with(" ") || line.ends_with("\t")) { + + if (!trimed_whitespace) { + text_editor->begin_complex_operation(); + trimed_whitespace = true; + } + + int end = 0; + for (int j = line.length() - 1; j > -1; j--) { + if (line[j] != ' ' && line[j] != '\t') { + end = j + 1; + break; + } + } + text_editor->set_line(i, line.substr(0, end)); + } + } + + if (trimed_whitespace) { + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_indent_to_spaces() { + int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); + String indent = ""; + + for (int i = 0; i < indent_size; i++) { + indent += " "; + } + + int cursor_line = text_editor->cursor_get_line(); + int cursor_column = text_editor->cursor_get_column(); + + bool changed_indentation = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + + if (line.length() <= 0) { + continue; + } + + int j = 0; + while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { + if (line[j] == '\t') { + if (!changed_indentation) { + text_editor->begin_complex_operation(); + changed_indentation = true; + } + if (cursor_line == i && cursor_column > j) { + cursor_column += indent_size - 1; + } + line = line.left(j) + indent + line.right(j + 1); + } + j++; + } + if (changed_indentation) { + text_editor->set_line(i, line); + } + } + if (changed_indentation) { + text_editor->cursor_set_column(cursor_column); + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_indent_to_tabs() { + int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); + indent_size -= 1; + + int cursor_line = text_editor->cursor_get_line(); + int cursor_column = text_editor->cursor_get_column(); + + bool changed_indentation = false; + for (int i = 0; i < text_editor->get_line_count(); i++) { + String line = text_editor->get_line(i); + + if (line.length() <= 0) { + continue; + } + + int j = 0; + int space_count = -1; + while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { + if (line[j] != '\t') { + space_count++; + + if (space_count == indent_size) { + if (!changed_indentation) { + text_editor->begin_complex_operation(); + changed_indentation = true; + } + if (cursor_line == i && cursor_column > j) { + cursor_column -= indent_size; + } + line = line.left(j - indent_size) + "\t" + line.right(j + 1); + j = 0; + space_count = -1; + } + } else { + space_count = -1; + } + j++; + } + if (changed_indentation) { + text_editor->set_line(i, line); + } + } + if (changed_indentation) { + text_editor->cursor_set_column(cursor_column); + text_editor->end_complex_operation(); + text_editor->update(); + } +} + +void CodeTextEditor::convert_case(CaseStyle p_case) { + if (!text_editor->is_selection_active()) { + return; + } + + text_editor->begin_complex_operation(); + + int begin = text_editor->get_selection_from_line(); + int end = text_editor->get_selection_to_line(); + int begin_col = text_editor->get_selection_from_column(); + int end_col = text_editor->get_selection_to_column(); + + for (int i = begin; i <= end; i++) { + int len = text_editor->get_line(i).length(); + if (i == end) + len -= len - end_col; + if (i == begin) + len -= begin_col; + String new_line = text_editor->get_line(i).substr(i == begin ? begin_col : 0, len); + + switch (p_case) { + case UPPER: { + new_line = new_line.to_upper(); + } break; + case LOWER: { + new_line = new_line.to_lower(); + } break; + case CAPITALIZE: { + new_line = new_line.capitalize(); + } break; + } + + if (i == begin) { + new_line = text_editor->get_line(i).left(begin_col) + new_line; + } + if (i == end) { + new_line = new_line + text_editor->get_line(i).right(end_col); + } + text_editor->set_line(i, new_line); + } + text_editor->end_complex_operation(); +} + +void CodeTextEditor::move_lines_up() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int from_line = text_editor->get_selection_from_line(); + int from_col = text_editor->get_selection_from_column(); + int to_line = text_editor->get_selection_to_line(); + int to_column = text_editor->get_selection_to_column(); + + for (int i = from_line; i <= to_line; i++) { + int line_id = i; + int next_id = i - 1; + + if (line_id == 0 || next_id < 0) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + int from_line_up = from_line > 0 ? from_line - 1 : from_line; + int to_line_up = to_line > 0 ? to_line - 1 : to_line; + text_editor->select(from_line_up, from_col, to_line_up, to_column); + } else { + int line_id = text_editor->cursor_get_line(); + int next_id = line_id - 1; + + if (line_id == 0 || next_id < 0) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::move_lines_down() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int from_line = text_editor->get_selection_from_line(); + int from_col = text_editor->get_selection_from_column(); + int to_line = text_editor->get_selection_to_line(); + int to_column = text_editor->get_selection_to_column(); + + for (int i = to_line; i >= from_line; i--) { + int line_id = i; + int next_id = i + 1; + + if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + int from_line_down = from_line < text_editor->get_line_count() ? from_line + 1 : from_line; + int to_line_down = to_line < text_editor->get_line_count() ? to_line + 1 : to_line; + text_editor->select(from_line_down, from_col, to_line_down, to_column); + } else { + int line_id = text_editor->cursor_get_line(); + int next_id = line_id + 1; + + if (line_id == text_editor->get_line_count() - 1 || next_id > text_editor->get_line_count()) + return; + + text_editor->unfold_line(line_id); + text_editor->unfold_line(next_id); + + text_editor->swap_lines(line_id, next_id); + text_editor->cursor_set_line(next_id); + } + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::delete_lines() { + text_editor->begin_complex_operation(); + if (text_editor->is_selection_active()) { + int to_line = text_editor->get_selection_to_line(); + int from_line = text_editor->get_selection_from_line(); + int count = Math::abs(to_line - from_line) + 1; + while (count) { + text_editor->set_line(text_editor->cursor_get_line(), ""); + text_editor->backspace_at_cursor(); + count--; + if (count) + text_editor->unfold_line(from_line); + } + text_editor->cursor_set_line(from_line - 1); + text_editor->deselect(); + } else { + int line = text_editor->cursor_get_line(); + text_editor->set_line(text_editor->cursor_get_line(), ""); + text_editor->backspace_at_cursor(); + text_editor->unfold_line(line); + text_editor->cursor_set_line(line); + } + text_editor->end_complex_operation(); +} + +void CodeTextEditor::code_lines_down() { + int from_line = text_editor->cursor_get_line(); + int to_line = text_editor->cursor_get_line(); + int column = text_editor->cursor_get_column(); + + if (text_editor->is_selection_active()) { + from_line = text_editor->get_selection_from_line(); + to_line = text_editor->get_selection_to_line(); + column = text_editor->cursor_get_column(); + } + int next_line = to_line + 1; + + if (to_line >= text_editor->get_line_count() - 1) { + text_editor->set_line(to_line, text_editor->get_line(to_line) + "\n"); + } + + text_editor->begin_complex_operation(); + for (int i = from_line; i <= to_line; i++) { + + text_editor->unfold_line(i); + if (i >= text_editor->get_line_count() - 1) { + text_editor->set_line(i, text_editor->get_line(i) + "\n"); + } + String line_clone = text_editor->get_line(i); + text_editor->insert_at(line_clone, next_line); + next_line++; + } + + text_editor->cursor_set_column(column); + if (text_editor->is_selection_active()) { + text_editor->select(to_line + 1, text_editor->get_selection_from_column(), next_line - 1, text_editor->get_selection_to_column()); + } + + text_editor->end_complex_operation(); + text_editor->update(); +} + +void CodeTextEditor::goto_line(int p_line) { + text_editor->deselect(); + text_editor->unfold_line(p_line); + text_editor->call_deferred("cursor_set_line", p_line); +} + +void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + text_editor->unfold_line(p_line); + text_editor->call_deferred("cursor_set_line", p_line); + text_editor->call_deferred("cursor_set_column", p_begin); + text_editor->select(p_line, p_begin, p_line, p_end); +} + +Variant CodeTextEditor::get_edit_state() { + Dictionary state; + + state["scroll_position"] = text_editor->get_v_scroll(); + state["column"] = text_editor->cursor_get_column(); + state["row"] = text_editor->cursor_get_line(); + + return state; +} + +void CodeTextEditor::set_edit_state(const Variant &p_state) { + Dictionary state = p_state; + text_editor->cursor_set_column(state["column"]); + text_editor->cursor_set_line(state["row"]); + text_editor->set_v_scroll(state["scroll_position"]); + text_editor->grab_focus(); +} + void CodeTextEditor::set_error(const String &p_error) { error->set_text(p_error); @@ -907,6 +1246,29 @@ CodeTextEditor::CodeTextEditor() { status_bar->add_child(memnew(Label)); //to keep the height if the other labels are not visible + warning_label = memnew(Label); + status_bar->add_child(warning_label); + warning_label->set_align(Label::ALIGN_RIGHT); + warning_label->set_valign(Label::VALIGN_CENTER); + warning_label->set_v_size_flags(SIZE_FILL); + warning_label->set_default_cursor_shape(CURSOR_POINTING_HAND); + warning_label->set_mouse_filter(MOUSE_FILTER_STOP); + warning_label->set_text(TTR("Warnings:")); + warning_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts")); + + warning_count_label = memnew(Label); + status_bar->add_child(warning_count_label); + warning_count_label->set_valign(Label::VALIGN_CENTER); + warning_count_label->set_v_size_flags(SIZE_FILL); + warning_count_label->set_autowrap(true); // workaround to prevent resizing the label on each change, do not touch + warning_count_label->set_clip_text(true); // workaround to prevent resizing the label on each change, do not touch + warning_count_label->set_custom_minimum_size(Size2(40, 1) * EDSCALE); + warning_count_label->set_align(Label::ALIGN_RIGHT); + warning_count_label->set_default_cursor_shape(CURSOR_POINTING_HAND); + warning_count_label->set_mouse_filter(MOUSE_FILTER_STOP); + warning_count_label->add_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_font("status_source", "EditorFonts")); + warning_count_label->set_text("0"); + Label *zoom_txt = memnew(Label); status_bar->add_child(zoom_txt); zoom_txt->set_align(Label::ALIGN_RIGHT); @@ -979,7 +1341,7 @@ CodeTextEditor::CodeTextEditor() { font_resize_val = 0; font_size = EditorSettings::get_singleton()->get("interface/editor/code_font_size"); - zoom_nb->set_text(itos(100 * font_size / 14) + "%"); + zoom_nb->set_text(itos(100 * font_size / (14 * EDSCALE)) + "%"); font_resize_timer = memnew(Timer); add_child(font_resize_timer); font_resize_timer->set_one_shot(true); diff --git a/editor/code_editor.h b/editor/code_editor.h index 2a3bb1ba76..ee47eff9a8 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -142,6 +142,8 @@ class CodeTextEditor : public VBoxContainer { TextEdit *text_editor; FindReplaceBar *find_replace_bar; HBoxContainer *status_bar; + Label *warning_label; + Label *warning_count_label; Label *line_nb; Label *col_nb; @@ -186,11 +188,36 @@ protected: static void _bind_methods(); public: + void trim_trailing_whitespace(); + + void convert_indent_to_spaces(); + void convert_indent_to_tabs(); + + enum CaseStyle { + UPPER, + LOWER, + CAPITALIZE, + }; + void convert_case(CaseStyle p_case); + + void move_lines_up(); + void move_lines_down(); + void delete_lines(); + void code_lines_down(); + + void goto_line(int p_line); + void goto_line_selection(int p_line, int p_begin, int p_end); + + Variant get_edit_state(); + void set_edit_state(const Variant &p_state); + void update_editor_settings(); void set_error(const String &p_error); void update_line_and_column() { _line_col_changed(); } TextEdit *get_text_edit() { return text_editor; } FindReplaceBar *get_find_replace_bar() { return find_replace_bar; } + Label *get_warning_label() const { return warning_label; } + Label *get_warning_count_label() const { return warning_count_label; } virtual void apply_code() {} void set_code_complete_func(CodeTextEditorCodeCompleteFunc p_code_complete_func, void *p_ud); diff --git a/editor/collada/collada.cpp b/editor/collada/collada.cpp index 734229d014..fa7e37eb1f 100644 --- a/editor/collada/collada.cpp +++ b/editor/collada/collada.cpp @@ -191,7 +191,7 @@ Transform Collada::Node::get_global_transform() const { return default_transform; } -Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) { +Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) const { ERR_FAIL_COND_V(keys.size() == 0, Vector<float>()); int i = 0; @@ -225,22 +225,22 @@ Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) { ret.resize(16); Transform tr; // i wonder why collada matrices are transposed, given that's opposed to opengl.. - ret[0] = interp.basis.elements[0][0]; - ret[1] = interp.basis.elements[0][1]; - ret[2] = interp.basis.elements[0][2]; - ret[4] = interp.basis.elements[1][0]; - ret[5] = interp.basis.elements[1][1]; - ret[6] = interp.basis.elements[1][2]; - ret[8] = interp.basis.elements[2][0]; - ret[9] = interp.basis.elements[2][1]; - ret[10] = interp.basis.elements[2][2]; - ret[3] = interp.origin.x; - ret[7] = interp.origin.y; - ret[11] = interp.origin.z; - ret[12] = 0; - ret[13] = 0; - ret[14] = 0; - ret[15] = 1; + ret.write[0] = interp.basis.elements[0][0]; + ret.write[1] = interp.basis.elements[0][1]; + ret.write[2] = interp.basis.elements[0][2]; + ret.write[4] = interp.basis.elements[1][0]; + ret.write[5] = interp.basis.elements[1][1]; + ret.write[6] = interp.basis.elements[1][2]; + ret.write[8] = interp.basis.elements[2][0]; + ret.write[9] = interp.basis.elements[2][1]; + ret.write[10] = interp.basis.elements[2][2]; + ret.write[3] = interp.origin.x; + ret.write[7] = interp.origin.y; + ret.write[11] = interp.origin.z; + ret.write[12] = 0; + ret.write[13] = 0; + ret.write[14] = 0; + ret.write[15] = 1; return ret; } else { @@ -249,7 +249,7 @@ Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) { dest.resize(keys[i].data.size()); for (int j = 0; j < dest.size(); j++) { - dest[j] = keys[i].data[j] * c + keys[i - 1].data[j] * (1.0 - c); + dest.write[j] = keys[i].data[j] * c + keys[i - 1].data[j] * (1.0 - c); } return dest; //interpolate one by one @@ -452,7 +452,7 @@ Transform Collada::_read_transform(XMLParser &parser) { Vector<float> farr; farr.resize(16); for (int i = 0; i < 16; i++) { - farr[i] = array[i].to_double(); + farr.write[i] = array[i].to_double(); } return _read_transform_from_array(farr); @@ -1104,7 +1104,7 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name int from = prim.indices.size(); prim.indices.resize(from + values.size()); for (int i = 0; i < values.size(); i++) - prim.indices[from + i] = values[i]; + prim.indices.write[from + i] = values[i]; } else if (prim.vertex_size > 0) { prim.indices = values; @@ -1884,7 +1884,7 @@ void Collada::_parse_animation(XMLParser &parser) { track.keys.resize(key_count); for (int j = 0; j < key_count; j++) { - track.keys[j].time = time_keys[j]; + track.keys.write[j].time = time_keys[j]; state.animation_length = MAX(state.animation_length, time_keys[j]); } @@ -1905,9 +1905,9 @@ void Collada::_parse_animation(XMLParser &parser) { ERR_CONTINUE((output.size() / stride) != key_count); for (int j = 0; j < key_count; j++) { - track.keys[j].data.resize(output_len); + track.keys.write[j].data.resize(output_len); for (int k = 0; k < output_len; k++) - track.keys[j].data[k] = output[l + j * stride + k]; //super weird but should work: + track.keys.write[j].data.write[k] = output[l + j * stride + k]; //super weird but should work: } if (sampler.has("INTERPOLATION")) { @@ -1919,9 +1919,9 @@ void Collada::_parse_animation(XMLParser &parser) { for (int j = 0; j < key_count; j++) { if (interps[j] == "BEZIER") - track.keys[j].interp_type = AnimationTrack::INTERP_BEZIER; + track.keys.write[j].interp_type = AnimationTrack::INTERP_BEZIER; else - track.keys[j].interp_type = AnimationTrack::INTERP_LINEAR; + track.keys.write[j].interp_type = AnimationTrack::INTERP_LINEAR; } } @@ -1939,8 +1939,8 @@ void Collada::_parse_animation(XMLParser &parser) { ERR_CONTINUE(outangents.size() != key_count * 2 * names.size()); for (int j = 0; j < key_count; j++) { - track.keys[j].in_tangent = Vector2(intangents[j * 2 * names.size() + 0 + l * 2], intangents[j * 2 * names.size() + 1 + l * 2]); - track.keys[j].out_tangent = Vector2(outangents[j * 2 * names.size() + 0 + l * 2], outangents[j * 2 * names.size() + 1 + l * 2]); + track.keys.write[j].in_tangent = Vector2(intangents[j * 2 * names.size() + 0 + l * 2], intangents[j * 2 * names.size() + 1 + l * 2]); + track.keys.write[j].out_tangent = Vector2(outangents[j * 2 * names.size() + 0 + l * 2], outangents[j * 2 * names.size() + 1 + l * 2]); } } @@ -2118,7 +2118,7 @@ void Collada::_joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner) { for (int i = 0; i < nj->children.size(); i++) { - _joint_set_owner(nj->children[i], p_owner); + _joint_set_owner(nj->children.write[i], p_owner); } } } @@ -2147,7 +2147,7 @@ void Collada::_create_skeletons(Collada::Node **p_node, NodeSkeleton *p_skeleton } for (int i = 0; i < node->children.size(); i++) { - _create_skeletons(&node->children[i], p_skeleton); + _create_skeletons(&node->children.write[i], p_skeleton); } } @@ -2314,7 +2314,7 @@ bool Collada::_optimize_skeletons(VisualScene *p_vscene, Node *p_node) { for (int i = 0; i < gp->children.size(); i++) { if (gp->children[i] == parent) { - gp->children[i] = node; + gp->children.write[i] = node; found = true; break; } @@ -2330,7 +2330,7 @@ bool Collada::_optimize_skeletons(VisualScene *p_vscene, Node *p_node) { if (p_vscene->root_nodes[i] == parent) { - p_vscene->root_nodes[i] = node; + p_vscene->root_nodes.write[i] = node; found = true; break; } @@ -2466,7 +2466,7 @@ void Collada::_optimize() { VisualScene &vs = E->get(); for (int i = 0; i < vs.root_nodes.size(); i++) { - _create_skeletons(&vs.root_nodes[i]); + _create_skeletons(&vs.root_nodes.write[i]); } for (int i = 0; i < vs.root_nodes.size(); i++) { diff --git a/editor/collada/collada.h b/editor/collada/collada.h index ec3284bc5c..7535162f74 100644 --- a/editor/collada/collada.h +++ b/editor/collada/collada.h @@ -312,7 +312,7 @@ public: total += weights[i].weight; if (total) for (int i = 0; i < 4; i++) - weights[i].weight /= total; + weights.write[i].weight /= total; } } @@ -515,7 +515,7 @@ public: Key() { interp_type = INTERP_LINEAR; } }; - Vector<float> get_value_at_time(float p_time); + Vector<float> get_value_at_time(float p_time) const; Vector<Key> keys; diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index c4f4e28fec..da73a3930a 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -51,7 +51,7 @@ public: if (name.begins_with("bind/")) { int which = name.get_slice("/", 1).to_int() - 1; ERR_FAIL_INDEX_V(which, params.size(), false); - params[which] = p_value; + params.write[which] = p_value; } else return false; diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 6b2a072e20..f97f1d0871 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -40,6 +40,11 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { + type_list.clear(); + ClassDB::get_class_list(&type_list); + ScriptServer::get_global_class_list(&type_list); + type_list.sort_custom<StringName::AlphCompare>(); + recent->clear(); FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::READ); @@ -173,10 +178,28 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p if (p_types.has(p_type)) return; - if (!ClassDB::is_parent_class(p_type, base_type) || p_type == base_type) + + bool cpp_type = ClassDB::class_exists(p_type); + EditorData &ed = EditorNode::get_singleton()->get_editor_data(); + + if (p_type == base_type) return; - String inherits = ClassDB::get_parent_class(p_type); + if (cpp_type) { + if (!ClassDB::is_parent_class(p_type, base_type)) + return; + } else { + if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type)) + return; + + String script_path = ScriptServer::get_global_class_path(p_type); + if (script_path.find("res://addons/", 0) != -1) { + if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) + return; + } + } + + String inherits = cpp_type ? ClassDB::get_parent_class(p_type) : ed.script_class_get_base(p_type); TreeItem *parent = p_root; @@ -189,17 +212,32 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p if (p_types.has(inherits)) parent = p_types[inherits]; + else if (ScriptServer::is_global_class(inherits)) + return; } + bool can_instance = (cpp_type && ClassDB::can_instance(p_type)) || ScriptServer::is_global_class(p_type); + TreeItem *item = search_options->create_item(parent); - item->set_text(0, p_type); - if (!ClassDB::can_instance(p_type)) { + if (cpp_type) { + item->set_text(0, p_type); + } else { + item->set_metadata(0, p_type); + item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); + } + if (!can_instance) { item->set_custom_color(0, get_color("disabled_font_color", "Editor")); item->set_selectable(0, false); } else { bool is_search_subsequence = search_box->get_text().is_subsequence_ofi(p_type); String to_select_type = *to_select ? (*to_select)->get_text(0) : ""; - bool current_item_is_preffered = ClassDB::is_parent_class(p_type, preferred_search_result_type) && !ClassDB::is_parent_class(to_select_type, preferred_search_result_type); + to_select_type = to_select_type.split(" ")[0]; + bool current_item_is_preffered; + if (cpp_type) { + current_item_is_preffered = ClassDB::is_parent_class(p_type, preferred_search_result_type) && !ClassDB::is_parent_class(to_select_type, preferred_search_result_type); + } else { + current_item_is_preffered = ed.script_class_is_parent(p_type, preferred_search_result_type) && !ed.script_class_is_parent(to_select_type, preferred_search_result_type); + } if (*to_select && p_type.length() < (*to_select)->get_text(0).length()) { current_item_is_preffered = true; } @@ -217,16 +255,19 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p // don't collapse the root node collapse &= (item != p_root); // don't collapse abstract nodes on the first tree level - collapse &= ((parent != p_root) || (ClassDB::can_instance(p_type))); + collapse &= ((parent != p_root) || (can_instance)); item->set_collapsed(collapse); } const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description; item->set_tooltip(0, description); - if (has_icon(p_type, "EditorIcons")) { + if (cpp_type && has_icon(p_type, "EditorIcons")) { item->set_icon(0, get_icon(p_type, "EditorIcons")); + } else if (!cpp_type && has_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")) { + + item->set_icon(0, get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")); } p_types[p_type] = item; @@ -243,47 +284,38 @@ void CreateDialog::_update_search() { _parse_fs(EditorFileSystem::get_singleton()->get_filesystem()); */ - List<StringName> global_classes; - ScriptServer::get_global_class_list(&global_classes); - - Map<String, List<String> > global_class_map; - for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - String base = ScriptServer::get_global_class_base(E->get()); - if (!global_class_map.has(base)) { - global_class_map[base] = List<String>(); - } - global_class_map[base].push_back(E->get()); - } - HashMap<String, TreeItem *> types; TreeItem *root = search_options->create_item(); + EditorData &ed = EditorNode::get_singleton()->get_editor_data(); root->set_text(0, base_type); if (has_icon(base_type, "EditorIcons")) { root->set_icon(0, get_icon(base_type, "EditorIcons")); } - List<StringName>::Element *I = type_list.front(); TreeItem *to_select = search_box->get_text() == base_type ? root : NULL; - for (; I; I = I->next()) { + for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { String type = I->get(); + bool cpp_type = ClassDB::class_exists(type); if (base_type == "Node" && type.begins_with("Editor")) continue; // do not show editor nodes - if (!ClassDB::can_instance(type)) + if (cpp_type && !ClassDB::can_instance(type)) continue; // can't create what can't be instanced bool skip = false; - for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) { - if (ClassDB::is_parent_class(type, E->get())) - skip = true; + if (cpp_type) { + for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) { + if (ClassDB::is_parent_class(type, E->get())) + skip = true; + } + if (skip) + continue; } - if (skip) - continue; if (search_box->get_text() == "") { add_type(type, types, root, &to_select); @@ -291,7 +323,7 @@ void CreateDialog::_update_search() { bool found = false; String type = I->get(); - while (type != "" && ClassDB::is_parent_class(type, base_type) && type != base_type) { + while (type != "" && (cpp_type ? ClassDB::is_parent_class(type, base_type) : ed.script_class_is_parent(type, base_type)) && type != base_type) { if (search_box->get_text().is_subsequence_ofi(type)) { found = true; @@ -305,32 +337,6 @@ void CreateDialog::_update_search() { add_type(I->get(), types, root, &to_select); } - if (global_class_map.has(type) && ClassDB::is_parent_class(type, base_type)) { - for (List<String>::Element *J = global_class_map[type].front(); J; J = J->next()) { - bool show = search_box->get_text().is_subsequence_ofi(J->get()); - - if (!show) - continue; - - if (!types.has(type)) - add_type(type, types, root, &to_select); - - TreeItem *ti; - if (types.has(type)) - ti = types[type]; - else - ti = search_options->get_root(); - - TreeItem *item = search_options->create_item(ti); - item->set_metadata(0, J->get()); - item->set_text(0, J->get() + " (" + ScriptServer::get_global_class_path(J->get()).get_file() + ")"); - item->set_icon(0, _get_editor_icon(type)); - if (!to_select || J->get() == search_box->get_text()) { - to_select = item; - } - } - } - if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) { //there are custom types based on this... cool. @@ -419,6 +425,8 @@ void CreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", this, "_confirmed"); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); favorite->set_icon(get_icon("Favorites", "EditorIcons")); } break; case NOTIFICATION_EXIT_TREE: { @@ -694,9 +702,6 @@ CreateDialog::CreateDialog() { is_replace_mode = false; - ClassDB::get_class_list(&type_list); - type_list.sort_custom<StringName::AlphCompare>(); - set_resizable(true); HSplitContainer *hsc = memnew(HSplitContainer); @@ -762,4 +767,6 @@ CreateDialog::CreateDialog() { type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix. + + EDITOR_DEF("interface/editors/derive_script_globals_by_name", true); } diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index 542dca74e0..91a29f5717 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -58,7 +58,7 @@ void DocData::merge_from(const DocData &p_data) { for (int i = 0; i < c.methods.size(); i++) { - MethodDoc &m = c.methods[i]; + MethodDoc &m = c.methods.write[i]; for (int j = 0; j < cf.methods.size(); j++) { @@ -72,13 +72,13 @@ void DocData::merge_from(const DocData &p_data) { Vector<bool> arg_used; arg_used.resize(arg_count); for (int l = 0; l < arg_count; ++l) - arg_used[l] = false; + arg_used.write[l] = false; // also there is no guarantee that argument ordering will match, so we // have to check one by one so we make sure we have an exact match for (int k = 0; k < arg_count; ++k) { for (int l = 0; l < arg_count; ++l) if (cf.methods[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) { - arg_used[l] = true; + arg_used.write[l] = true; break; } } @@ -98,7 +98,7 @@ void DocData::merge_from(const DocData &p_data) { for (int i = 0; i < c.signals.size(); i++) { - MethodDoc &m = c.signals[i]; + MethodDoc &m = c.signals.write[i]; for (int j = 0; j < cf.signals.size(); j++) { @@ -113,7 +113,7 @@ void DocData::merge_from(const DocData &p_data) { for (int i = 0; i < c.constants.size(); i++) { - ConstantDoc &m = c.constants[i]; + ConstantDoc &m = c.constants.write[i]; for (int j = 0; j < cf.constants.size(); j++) { @@ -128,7 +128,7 @@ void DocData::merge_from(const DocData &p_data) { for (int i = 0; i < c.properties.size(); i++) { - PropertyDoc &p = c.properties[i]; + PropertyDoc &p = c.properties.write[i]; for (int j = 0; j < cf.properties.size(); j++) { @@ -146,7 +146,7 @@ void DocData::merge_from(const DocData &p_data) { for (int i = 0; i < c.theme_properties.size(); i++) { - PropertyDoc &p = c.theme_properties[i]; + PropertyDoc &p = c.theme_properties.write[i]; for (int j = 0; j < cf.theme_properties.size(); j++) { @@ -535,13 +535,14 @@ void DocData::generate(bool p_basic_types) { } List<StringName> constants; - Variant::get_numeric_constants_for_type(Variant::Type(i), &constants); + Variant::get_constants_for_type(Variant::Type(i), &constants); for (List<StringName>::Element *E = constants.front(); E; E = E->next()) { ConstantDoc constant; constant.name = E->get(); - constant.value = itos(Variant::get_numeric_constant_value(Variant::Type(i), E->get())); + Variant value = Variant::get_constant_value(Variant::Type(i), E->get()); + constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string(); c.constants.push_back(constant); } } @@ -1020,7 +1021,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri for (int i = 0; i < c.methods.size(); i++) { - MethodDoc &m = c.methods[i]; + const MethodDoc &m = c.methods[i]; String qualifiers; if (m.qualifiers != "") @@ -1040,7 +1041,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri for (int j = 0; j < m.arguments.size(); j++) { - ArgumentDoc &a = m.arguments[j]; + const ArgumentDoc &a = m.arguments[j]; String enum_text; if (a.enumeration != String()) { @@ -1075,7 +1076,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri if (c.properties[i].enumeration != String()) { enum_text = " enum=\"" + c.properties[i].enumeration + "\""; } - PropertyDoc &p = c.properties[i]; + const PropertyDoc &p = c.properties[i]; _write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + enum_text + ">"); _write_string(f, 3, p.description.strip_edges().xml_escape()); _write_string(f, 2, "</member>"); @@ -1090,11 +1091,11 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri _write_string(f, 1, "<signals>"); for (int i = 0; i < c.signals.size(); i++) { - MethodDoc &m = c.signals[i]; + const MethodDoc &m = c.signals[i]; _write_string(f, 2, "<signal name=\"" + m.name + "\">"); for (int j = 0; j < m.arguments.size(); j++) { - ArgumentDoc &a = m.arguments[j]; + const ArgumentDoc &a = m.arguments[j]; _write_string(f, 3, "<argument index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\">"); _write_string(f, 3, "</argument>"); } @@ -1113,7 +1114,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri for (int i = 0; i < c.constants.size(); i++) { - ConstantDoc &k = c.constants[i]; + const ConstantDoc &k = c.constants[i]; if (k.enumeration != String()) { _write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">"); } else { @@ -1132,7 +1133,7 @@ Error DocData::save_classes(const String &p_default_path, const Map<String, Stri _write_string(f, 1, "<theme_items>"); for (int i = 0; i < c.theme_properties.size(); i++) { - PropertyDoc &p = c.theme_properties[i]; + const PropertyDoc &p = c.theme_properties[i]; _write_string(f, 2, "<theme_item name=\"" + p.name + "\" type=\"" + p.type + "\">"); _write_string(f, 2, "</theme_item>"); } diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 4b09db0a9e..e4602f0f94 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -113,7 +113,7 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St EditorAbout::EditorAbout() { set_title(TTR("Thanks from the Godot community!")); - get_ok()->set_text(TTR("Thanks!")); + get_ok()->set_text(TTR("OK")); set_hide_on_ok(true); set_resizable(true); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 2f0982e5d9..d12c85861b 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -598,7 +598,7 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant & int i = 0; for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { - orders[i++] = E->get().order; + orders.write[i++] = E->get().order; } orders.sort(); diff --git a/editor/editor_builders.py b/editor/editor_builders.py new file mode 100644 index 0000000000..6c2f9e298e --- /dev/null +++ b/editor/editor_builders.py @@ -0,0 +1,412 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +import os.path +from platform_methods import subprocess_main +from compat import encode_utf8, byte_to_str, open_utf8, escape_string + + +def make_certs_header(target, source, env): + + src = source[0] + dst = target[0] + f = open(src, "rb") + g = open_utf8(dst, "w") + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _CERTS_RAW_H\n") + g.write("#define _CERTS_RAW_H\n") + g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _certs_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + g.close() + f.close() + + +def make_doc_header(target, source, env): + + dst = target[0] + g = open_utf8(dst, "w") + buf = "" + docbegin = "" + docend = "" + for src in source: + if not src.endswith(".xml"): + continue + with open_utf8(src, "r") as f: + content = f.read() + buf += content + + buf = encode_utf8(docbegin + buf + docend) + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _DOC_DATA_RAW_H\n") + g.write("#define _DOC_DATA_RAW_H\n") + g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _doc_data_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + g.write("};\n") + + g.write("#endif") + + g.close() + + +def make_fonts_header(target, source, env): + + dst = target[0] + + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_FONTS_H\n") + g.write("#define _EDITOR_FONTS_H\n") + + # saving uncompressed, since freetype will reference from memory pointer + xl_names = [] + for i in range(len(source)): + with open(source[i], "rb")as f: + buf = f.read() + + name = os.path.splitext(os.path.basename(source[i]))[0] + + g.write("static const int _font_" + name + "_size = " + str(len(buf)) + ";\n") + g.write("static const unsigned char _font_" + name + "[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + + g.write("};\n") + + g.write("#endif") + + g.close() + + +def make_translations_header(target, source, env): + + dst = target[0] + + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_TRANSLATIONS_H\n") + g.write("#define _EDITOR_TRANSLATIONS_H\n") + + import zlib + import os.path + + sorted_paths = sorted(source, key=lambda path: os.path.splitext(os.path.basename(path))[0]) + + xl_names = [] + for i in range(len(sorted_paths)): + with open(sorted_paths[i], "rb") as f: + buf = f.read() + decomp_size = len(buf) + buf = zlib.compress(buf) + name = os.path.splitext(os.path.basename(sorted_paths[i]))[0] + + g.write("static const unsigned char _translation_" + name + "_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + + g.write("};\n") + + xl_names.append([name, len(buf), str(decomp_size)]) + + g.write("struct EditorTranslationList {\n") + g.write("\tconst char* lang;\n") + g.write("\tint comp_size;\n") + g.write("\tint uncomp_size;\n") + g.write("\tconst unsigned char* data;\n") + g.write("};\n\n") + g.write("static EditorTranslationList _editor_translations[] = {\n") + for x in xl_names: + g.write("\t{ \"" + x[0] + "\", " + str(x[1]) + ", " + str(x[2]) + ", _translation_" + x[0] + "_compressed},\n") + g.write("\t{NULL, 0, 0, NULL}\n") + g.write("};\n") + + g.write("#endif") + + g.close() + + +def make_authors_header(target, source, env): + + sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] + sections_id = ["dev_founders", "dev_lead", "dev_manager", "dev_names"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_AUTHORS_H\n") + g.write("#define _EDITOR_AUTHORS_H\n") + + current_section = "" + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for i in range(len(sections)): + if line.strip().endswith(sections[i]): + current_section = escape_string(sections_id[i]) + reading = True + g.write("static const char *" + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + +def make_donors_header(target, source, env): + + sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", "Gold donors", "Silver donors", "Bronze donors"] + sections_id = ["donor_s_plat", "donor_s_gold", "donor_s_mini", "donor_gold", "donor_silver", "donor_bronze"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_DONORS_H\n") + g.write("#define _EDITOR_DONORS_H\n") + + current_section = "" + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for i in range(len(sections)): + if line.strip().endswith(sections[i]): + current_section = escape_string(sections_id[i]) + reading = True + g.write("static const char *" + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_license_header(target, source, env): + + src_copyright = source[0] + src_license = source[1] + dst = target[0] + f = open_utf8(src_license, "r") + fc = open_utf8(src_copyright, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_LICENSE_H\n") + g.write("#define _EDITOR_LICENSE_H\n") + g.write("static const char *about_license =") + + for line in f: + escaped_string = escape_string(line.strip()) + g.write("\n\t\"" + escaped_string + "\\n\"") + + g.write(";\n") + + tp_current = 0 + tp_file = "" + tp_comment = "" + tp_copyright = "" + tp_license = "" + + tp_licensename = "" + tp_licensebody = "" + + tp = [] + tp_licensetext = [] + for line in fc: + if line.startswith("#"): + continue + + if line.startswith("Files:"): + tp_file = line[6:].strip() + tp_current = 1 + elif line.startswith("Comment:"): + tp_comment = line[8:].strip() + tp_current = 2 + elif line.startswith("Copyright:"): + tp_copyright = line[10:].strip() + tp_current = 3 + elif line.startswith("License:"): + if tp_current != 0: + tp_license = line[8:].strip() + tp_current = 4 + else: + tp_licensename = line[8:].strip() + tp_current = 5 + elif line.startswith(" "): + if tp_current == 1: + tp_file += "\n" + line.strip() + elif tp_current == 3: + tp_copyright += "\n" + line.strip() + elif tp_current == 5: + if line.strip() == ".": + tp_licensebody += "\n" + else: + tp_licensebody += line[1:] + else: + if tp_current != 0: + if tp_current == 5: + tp_licensetext.append([tp_licensename, tp_licensebody]) + + tp_licensename = "" + tp_licensebody = "" + else: + added = False + for i in tp: + if i[0] == tp_comment: + i[1].append([tp_file, tp_copyright, tp_license]) + added = True + break + if not added: + tp.append([tp_comment,[[tp_file, tp_copyright, tp_license]]]) + + tp_file = [] + tp_comment = "" + tp_copyright = [] + tp_license = "" + tp_current = 0 + + tp_licensetext.append([tp_licensename, tp_licensebody]) + + about_thirdparty = "" + about_tp_copyright_count = "" + about_tp_license = "" + about_tp_copyright = "" + about_tp_file = "" + + for i in tp: + about_thirdparty += "\t\"" + i[0] + "\",\n" + about_tp_copyright_count += str(len(i[1])) + ", " + for j in i[1]: + file_body = "" + copyright_body = "" + for k in j[0].split("\n"): + if file_body != "": + file_body += "\\n\"\n" + escaped_string = escape_string(k.strip()) + file_body += "\t\"" + escaped_string + for k in j[1].split("\n"): + if copyright_body != "": + copyright_body += "\\n\"\n" + escaped_string = escape_string(k.strip()) + copyright_body += "\t\"" + escaped_string + + about_tp_file += "\t" + file_body + "\",\n" + about_tp_copyright += "\t" + copyright_body + "\",\n" + about_tp_license += "\t\"" + j[2] + "\",\n" + + about_license_name = "" + about_license_body = "" + + for i in tp_licensetext: + body = "" + for j in i[1].split("\n"): + if body != "": + body += "\\n\"\n" + escaped_string = escape_string(j.strip()) + body += "\t\"" + escaped_string + + about_license_name += "\t\"" + i[0] + "\",\n" + about_license_body += "\t" + body + "\",\n" + + g.write("static const char *about_thirdparty[] = {\n") + g.write(about_thirdparty) + g.write("\t0\n") + g.write("};\n") + g.write("#define THIRDPARTY_COUNT " + str(len(tp)) + "\n") + + g.write("static const int about_tp_copyright_count[] = {\n\t") + g.write(about_tp_copyright_count) + g.write("0\n};\n") + + g.write("static const char *about_tp_file[] = {\n") + g.write(about_tp_file) + g.write("\t0\n") + g.write("};\n") + + g.write("static const char *about_tp_copyright[] = {\n") + g.write(about_tp_copyright) + g.write("\t0\n") + g.write("};\n") + + g.write("static const char *about_tp_license[] = {\n") + g.write(about_tp_license) + g.write("\t0\n") + g.write("};\n") + + g.write("static const char *about_license_name[] = {\n") + g.write(about_license_name) + g.write("\t0\n") + g.write("};\n") + g.write("#define LICENSE_COUNT " + str(len(tp_licensetext)) + "\n") + + g.write("static const char *about_license_body[] = {\n") + g.write(about_license_body) + g.write("\t0\n") + g.write("};\n") + + g.write("#endif\n") + + g.close() + fc.close() + f.close() + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 4dde893c6d..f4ef11eb36 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -62,7 +62,7 @@ void EditorHistory::cleanup_history() { fail = true; } else { //after level, clip - history[i].path.resize(j); + history.write[i].path.resize(j); } break; @@ -100,14 +100,14 @@ void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int if (p_property != "" && has_prev) { //add a sub property - History &pr = history[current]; + History &pr = history.write[current]; h = pr; h.path.resize(h.level + 1); h.path.push_back(o); h.level++; } else if (p_level_change != -1 && has_prev) { //add a sub property - History &pr = history[current]; + History &pr = history.write[current]; h = pr; ERR_FAIL_INDEX(p_level_change, h.path.size()); h.level = p_level_change; @@ -206,7 +206,7 @@ ObjectID EditorHistory::get_current() { if (current < 0 || current >= history.size()) return 0; - History &h = history[current]; + History &h = history.write[current]; Object *obj = ObjectDB::get_instance(h.path[h.level].object); if (!obj) return 0; @@ -558,7 +558,7 @@ void EditorData::move_edited_scene_index(int p_idx, int p_to_idx) { ERR_FAIL_INDEX(p_idx, edited_scene.size()); ERR_FAIL_INDEX(p_to_idx, edited_scene.size()); - SWAP(edited_scene[p_idx], edited_scene[p_to_idx]); + SWAP(edited_scene.write[p_idx], edited_scene.write[p_to_idx]); } void EditorData::remove_scene(int p_idx) { ERR_FAIL_INDEX(p_idx, edited_scene.size()); @@ -644,7 +644,7 @@ bool EditorData::check_and_update_scene(int p_idx) { //transfer selection List<Node *> new_selection; - for (List<Node *>::Element *E = edited_scene[p_idx].selection.front(); E; E = E->next()) { + for (List<Node *>::Element *E = edited_scene.write[p_idx].selection.front(); E; E = E->next()) { NodePath p = edited_scene[p_idx].root->get_path_to(E->get()); Node *new_node = new_scene->get_node(p); if (new_node) @@ -654,8 +654,8 @@ bool EditorData::check_and_update_scene(int p_idx) { new_scene->set_filename(edited_scene[p_idx].root->get_filename()); memdelete(edited_scene[p_idx].root); - edited_scene[p_idx].root = new_scene; - edited_scene[p_idx].selection = new_selection; + edited_scene.write[p_idx].root = new_scene; + edited_scene.write[p_idx].selection = new_selection; return true; } @@ -685,7 +685,7 @@ Node *EditorData::get_edited_scene_root(int p_idx) { void EditorData::set_edited_scene_root(Node *p_root) { ERR_FAIL_INDEX(current_edited_scene, edited_scene.size()); - edited_scene[current_edited_scene].root = p_root; + edited_scene.write[current_edited_scene].root = p_root; } int EditorData::get_edited_scene_count() const { @@ -707,10 +707,10 @@ Vector<EditorData::EditedScene> EditorData::get_edited_scenes() const { void EditorData::set_edited_scene_version(uint64_t version, int p_scene_idx) { ERR_FAIL_INDEX(current_edited_scene, edited_scene.size()); if (p_scene_idx < 0) { - edited_scene[current_edited_scene].version = version; + edited_scene.write[current_edited_scene].version = version; } else { ERR_FAIL_INDEX(p_scene_idx, edited_scene.size()); - edited_scene[p_scene_idx].version = version; + edited_scene.write[p_scene_idx].version = version; } } @@ -793,7 +793,7 @@ String EditorData::get_scene_path(int p_idx) const { void EditorData::set_edited_scene_live_edit_root(const NodePath &p_root) { ERR_FAIL_INDEX(current_edited_scene, edited_scene.size()); - edited_scene[current_edited_scene].live_edit_root = p_root; + edited_scene.write[current_edited_scene].live_edit_root = p_root; } NodePath EditorData::get_edited_scene_live_edit_root() { @@ -806,7 +806,7 @@ void EditorData::save_edited_scene_state(EditorSelection *p_selection, EditorHis ERR_FAIL_INDEX(current_edited_scene, edited_scene.size()); - EditedScene &es = edited_scene[current_edited_scene]; + EditedScene &es = edited_scene.write[current_edited_scene]; es.selection = p_selection->get_selected_node_list(); es.history_current = p_history->current; es.history_stored = p_history->history; @@ -817,7 +817,7 @@ void EditorData::save_edited_scene_state(EditorSelection *p_selection, EditorHis Dictionary EditorData::restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history) { ERR_FAIL_INDEX_V(current_edited_scene, edited_scene.size(), Dictionary()); - EditedScene &es = edited_scene[current_edited_scene]; + EditedScene &es = edited_scene.write[current_edited_scene]; p_history->current = es.history_current; p_history->history = es.history_stored; @@ -853,6 +853,41 @@ void EditorData::get_plugin_window_layout(Ref<ConfigFile> p_layout) { } } +bool EditorData::script_class_is_parent(const String &p_class, const String &p_inherits) { + if (!ScriptServer::is_global_class(p_class)) + return false; + String base = script_class_get_base(p_class); + while (p_inherits != base) { + if (ClassDB::class_exists(base)) { + return ClassDB::is_parent_class(base, p_inherits); + } else if (ScriptServer::is_global_class(base)) { + base = script_class_get_base(base); + } else { + return false; + } + } + return true; +} + +StringName EditorData::script_class_get_base(const String &p_class) { + + if (!ScriptServer::is_global_class(p_class)) + return StringName(); + + String path = ScriptServer::get_global_class_path(p_class); + + Ref<Script> script = ResourceLoader::load(path, "Script"); + if (script.is_null()) + return StringName(); + + Ref<Script> base_script = script->get_base_script(); + if (base_script.is_null()) { + return ScriptServer::get_global_class_base(p_class); + } + + return script->get_language()->get_global_class_name(base_script->get_path()); +} + EditorData::EditorData() { current_edited_scene = -1; diff --git a/editor/editor_data.h b/editor/editor_data.h index 0ecef8ae31..fac6635cd2 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -211,6 +211,9 @@ public: void notify_edited_scene_changed(); void notify_resource_saved(const Ref<Resource> &p_resource); + bool script_class_is_parent(const String &p_class, const String &p_inherits); + StringName script_class_get_base(const String &p_class); + EditorData(); }; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 317fffad64..5c911a00ca 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -187,7 +187,7 @@ void EditorExportPreset::remove_patch(int p_idx) { void EditorExportPreset::set_patch(int p_index, const String &p_path) { ERR_FAIL_INDEX(p_index, patches.size()); - patches[p_index] = p_path; + patches.write[p_index] = p_path; EditorExport::singleton->save_presets(); } String EditorExportPreset::get_patch(int p_index) { @@ -295,7 +295,7 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa MD5Final(&ctx); sd.md5.resize(16); for (int i = 0; i < 16; i++) { - sd.md5[i] = ctx.digest[i]; + sd.md5.write[i] = ctx.digest[i]; } } @@ -584,9 +584,9 @@ EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_pla //initial export plugin callback for (int i = 0; i < export_plugins.size(); i++) { if (export_plugins[i]->get_script_instance()) { //script based - export_plugins[i]->_export_begin_script(features.features_pv, p_debug, p_path, p_flags); + export_plugins.write[i]->_export_begin_script(features.features_pv, p_debug, p_path, p_flags); } else { - export_plugins[i]->_export_begin(features.features, p_debug, p_path, p_flags); + export_plugins.write[i]->_export_begin(features.features, p_debug, p_path, p_flags); } } } @@ -594,7 +594,7 @@ EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_pla EditorExportPlatform::ExportNotifier::~ExportNotifier() { Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); for (int i = 0; i < export_plugins.size(); i++) { - export_plugins[i]->_export_end(); + export_plugins.write[i]->_export_end(); } } @@ -632,7 +632,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size()); } - export_plugins[i]->_clear(); + export_plugins.write[i]->_clear(); } FeatureContainers feature_containers = get_feature_containers(p_preset); @@ -687,9 +687,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & bool do_export = true; for (int i = 0; i < export_plugins.size(); i++) { if (export_plugins[i]->get_script_instance()) { //script based - export_plugins[i]->_export_file_script(path, type, features_pv); + export_plugins.write[i]->_export_file_script(path, type, features_pv); } else { - export_plugins[i]->_export_file(path, type, features); + export_plugins.write[i]->_export_file(path, type, features); } if (p_so_func) { for (int j = 0; j < export_plugins[i]->shared_objects.size(); j++) { @@ -709,7 +709,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (export_plugins[i]->skipped) { do_export = false; } - export_plugins[i]->_clear(); + export_plugins.write[i]->_clear(); if (!do_export) break; //apologies, not exporting @@ -751,7 +751,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<uint8_t> new_file; new_file.resize(utf8.length()); for (int j = 0; j < utf8.length(); j++) { - new_file[j] = utf8[j]; + new_file.write[j] = utf8[j]; } p_func(p_udata, from + ".remap", new_file, idx, total); @@ -1130,7 +1130,7 @@ void EditorExport::load_config() { for (int i = 0; i < export_platforms.size(); i++) { if (export_platforms[i]->get_name() == platform) { - preset = export_platforms[i]->create_preset(); + preset = export_platforms.write[i]->create_preset(); break; } } @@ -1203,7 +1203,7 @@ bool EditorExport::poll_export_platforms() { bool changed = false; for (int i = 0; i < export_platforms.size(); i++) { - if (export_platforms[i]->poll_devices()) { + if (export_platforms.write[i]->poll_devices()) { changed = true; } } diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 8a8a21543b..7d56c4985a 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1135,7 +1135,7 @@ void EditorFileDialog::_favorite_move_up() { if (a_idx == -1 || b_idx == -1) return; - SWAP(favorited[a_idx], favorited[b_idx]); + SWAP(favorited.write[a_idx], favorited.write[b_idx]); EditorSettings::get_singleton()->set_favorite_dirs(favorited); @@ -1155,7 +1155,7 @@ void EditorFileDialog::_favorite_move_down() { if (a_idx == -1 || b_idx == -1) return; - SWAP(favorited[a_idx], favorited[b_idx]); + SWAP(favorited.write[a_idx], favorited.write[b_idx]); EditorSettings::get_singleton()->set_favorite_dirs(favorited); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 727383b960..1ac6eef8b4 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -253,7 +253,8 @@ void EditorHelpSearch::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); connect("confirmed", this, "_confirmed"); _update_search(); @@ -267,7 +268,8 @@ void EditorHelpSearch::_notification(int p_what) { } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } else if (p_what == NOTIFICATION_PROCESS) { if (search.is_valid()) { @@ -381,7 +383,8 @@ void EditorHelpIndex::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); _update_class_list(); connect("confirmed", this, "_tree_item_selected"); @@ -392,7 +395,8 @@ void EditorHelpIndex::_notification(int p_what) { } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } } @@ -541,6 +545,7 @@ void EditorHelp::_class_desc_select(const String &p_select) { String class_name; if (select.find(".") != -1) { class_name = select.get_slice(".", 0); + select = select.get_slice(".", 1); } else { class_name = "@GlobalScope"; } @@ -1214,6 +1219,18 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { constant_line[constants[i].name] = class_desc->get_line_count() - 2; class_desc->push_font(doc_code_font); + + if (constants[i].value.begins_with("Color(") && constants[i].value.ends_with(")")) { + String stripped = constants[i].value.replace(" ", "").replace("Color(", "").replace(")", ""); + Vector<float> color = stripped.split_floats(","); + if (color.size() >= 3) { + class_desc->push_color(Color(color[0], color[1], color[2])); + static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; + class_desc->add_text(String(prefix)); + class_desc->pop(); + } + } + class_desc->push_color(headline_color); _add_text(constants[i].name); class_desc->pop(); @@ -1223,6 +1240,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->push_color(value_color); _add_text(constants[i].value); class_desc->pop(); + class_desc->pop(); if (constants[i].description != "") { class_desc->push_font(doc_font); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 488980db07..99a2b2aa67 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -43,6 +43,9 @@ Size2 EditorProperty::get_minimum_size() const { Size2 ms; + Ref<Font> font = get_font("font", "Tree"); + ms.height = font->get_height(); + for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -70,12 +73,10 @@ Size2 EditorProperty::get_minimum_size() const { ms.width += check->get_width() + get_constant("hseparator", "Tree"); } - if (bottom_editor != NULL) { - Ref<Font> font = get_font("font", "Tree"); - ms.height += font->get_height(); + if (bottom_editor != NULL && bottom_editor->is_visible()) { ms.height += get_constant("vseparation", "Tree"); Size2 bems = bottom_editor->get_combined_minimum_size(); - bems.width += get_constant("item_margin", "Tree"); + //bems.width += get_constant("item_margin", "Tree"); ms.height += bems.height; ms.width = MAX(ms.width, bems.width); } @@ -98,6 +99,7 @@ void EditorProperty::_notification(int p_what) { int child_room = size.width * (1.0 - split_ratio); Ref<Font> font = get_font("font", "Tree"); int height = font->get_height(); + bool no_children = true; //compute room needed for (int i = 0; i < get_child_count(); i++) { @@ -113,11 +115,16 @@ void EditorProperty::_notification(int p_what) { Size2 minsize = c->get_combined_minimum_size(); child_room = MAX(child_room, minsize.width); height = MAX(height, minsize.height); + no_children = false; } - text_size = MAX(0, size.width - child_room + 4 * EDSCALE); - - rect = Rect2(text_size, 0, size.width - text_size, height); + if (no_children) { + text_size = size.width; + rect = Rect2(size.width - 1, 0, 1, height); + } else { + text_size = MAX(0, size.width - (child_room + 4 * EDSCALE)); + rect = Rect2(size.width - child_room, 0, child_room, height); + } if (bottom_editor) { @@ -178,7 +185,7 @@ void EditorProperty::_notification(int p_what) { draw_style_box(sb, Rect2(Vector2(), size)); } - if (right_child_rect != Rect2()) { + if (draw_top_bg && right_child_rect != Rect2()) { draw_rect(right_child_rect, dark_color); } if (bottom_child_rect != Rect2()) { @@ -189,7 +196,7 @@ void EditorProperty::_notification(int p_what) { if (draw_red) { color = get_color("error_color", "Editor"); } else { - color = get_color("font_color", "Tree"); + color = get_color("property_color", "Editor"); } if (label.find(".") != -1) { color.a = 0.5; //this should be un-hacked honestly, as it's used for editor overrides @@ -787,6 +794,8 @@ void EditorProperty::_bind_methods() { EditorProperty::EditorProperty() { + draw_top_bg = true; + object = NULL; split_ratio = 0.5; selectable = true; text_size = 0; @@ -1613,32 +1622,10 @@ void EditorInspector::update_tree() { for (List<EditorInspectorPlugin::AddedEditor>::Element *F = editors.front(); F; F = F->next()) { EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor); - current_vbox->add_child(F->get().property_editor); if (ep) { - + //set all this before the control gets the ENTER_TREE notification ep->object = object; - ep->connect("property_changed", this, "_property_changed"); - if (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED) { - ep->connect("property_changed", this, "_property_changed_update_all", varray(), CONNECT_DEFERRED); - } - ep->connect("property_keyed", this, "_property_keyed"); - ep->connect("property_keyed_with_value", this, "_property_keyed_with_value"); - ep->connect("property_checked", this, "_property_checked"); - ep->connect("selected", this, "_property_selected"); - ep->connect("multiple_properties_changed", this, "_multiple_properties_changed"); - ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED); - ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED); - if (doc_hint != String()) { - ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); - } else { - ep->set_tooltip(property_prefix + p.name); - } - ep->set_draw_red(draw_red); - ep->set_use_folding(use_folding); - ep->set_checkable(checkable); - ep->set_checked(checked); - ep->set_keying(keying); if (F->get().properties.size()) { @@ -1664,8 +1651,35 @@ void EditorInspector::update_tree() { editor_property_map[prop].push_back(ep); } } + ep->set_draw_red(draw_red); + ep->set_use_folding(use_folding); + ep->set_checkable(checkable); + ep->set_checked(checked); + ep->set_keying(keying); ep->set_read_only(read_only); + } + + current_vbox->add_child(F->get().property_editor); + + if (ep) { + + ep->connect("property_changed", this, "_property_changed"); + if (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED) { + ep->connect("property_changed", this, "_property_changed_update_all", varray(), CONNECT_DEFERRED); + } + ep->connect("property_keyed", this, "_property_keyed"); + ep->connect("property_keyed_with_value", this, "_property_keyed_with_value"); + ep->connect("property_checked", this, "_property_checked"); + ep->connect("selected", this, "_property_selected"); + ep->connect("multiple_properties_changed", this, "_multiple_properties_changed"); + ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED); + ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED); + if (doc_hint != String()) { + ep->set_tooltip(property_prefix + p.name + "::" + doc_hint); + } else { + ep->set_tooltip(property_prefix + p.name); + } ep->update_property(); ep->update_reload_status(); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 454622d662..ebe2124a40 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -68,6 +68,7 @@ private: bool can_revert; bool use_folding; + bool draw_top_bg; bool _might_be_in_instance(); bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage); @@ -149,6 +150,8 @@ public: String get_tooltip_text() const; + void set_draw_top_bg(bool p_draw) { draw_top_bg = p_draw; } + EditorProperty(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ff97878e71..81b7a66361 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -62,6 +62,8 @@ #include "editor/import/editor_scene_importer_gltf.h" #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_csv_translation.h" +#include "editor/import/resource_importer_image.h" +#include "editor/import/resource_importer_layered_texture.h" #include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" @@ -108,10 +110,12 @@ #include "editor/plugins/shader_graph_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" #include "editor/plugins/skeleton_editor_plugin.h" +#include "editor/plugins/skeleton_ik_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" #include "editor/plugins/sprite_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" #include "editor/plugins/style_box_editor_plugin.h" +#include "editor/plugins/text_editor.h" #include "editor/plugins/texture_editor_plugin.h" #include "editor/plugins/texture_region_editor_plugin.h" #include "editor/plugins/theme_editor_plugin.h" @@ -156,7 +160,6 @@ void EditorNode::_update_scene_tabs() { scene_tabs->set_current_tab(editor_data.get_edited_scene()); - int current = editor_data.get_edited_scene(); if (scene_tabs->get_offset_buttons_visible()) { // move add button to fixed position on the tabbar if (scene_tab_add->get_parent() == scene_tabs) { @@ -598,7 +601,7 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); if (err != OK) { - show_accept(TTR("Error saving resource!"), TTR("I see...")); + show_accept(TTR("Error saving resource!"), TTR("OK")); return; } @@ -688,15 +691,15 @@ void EditorNode::_dialog_display_save_error(String p_file, Error p_error) { case ERR_FILE_CANT_WRITE: { - show_accept(TTR("Can't open file for writing:") + " " + p_file.get_extension(), TTR("I see...")); + show_accept(TTR("Can't open file for writing:") + " " + p_file.get_extension(), TTR("OK")); } break; case ERR_FILE_UNRECOGNIZED: { - show_accept(TTR("Requested file format unknown:") + " " + p_file.get_extension(), TTR("I see...")); + show_accept(TTR("Requested file format unknown:") + " " + p_file.get_extension(), TTR("OK")); } break; default: { - show_accept(TTR("Error while saving."), TTR("I see...")); + show_accept(TTR("Error while saving."), TTR("OK")); } break; } } @@ -710,23 +713,23 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) { case ERR_CANT_OPEN: { - show_accept(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("I see...")); + show_accept(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_file.get_file()), TTR("OK")); } break; case ERR_PARSE_ERROR: { - show_accept(vformat(TTR("Error while parsing '%s'."), p_file.get_file()), TTR("I see...")); + show_accept(vformat(TTR("Error while parsing '%s'."), p_file.get_file()), TTR("OK")); } break; case ERR_FILE_CORRUPT: { - show_accept(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()), TTR("I see...")); + show_accept(vformat(TTR("Unexpected end of file '%s'."), p_file.get_file()), TTR("OK")); } break; case ERR_FILE_NOT_FOUND: { - show_accept(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()), TTR("I see...")); + show_accept(vformat(TTR("Missing '%s' or its dependencies."), p_file.get_file()), TTR("OK")); } break; default: { - show_accept(vformat(TTR("Error while loading '%s'."), p_file.get_file()), TTR("I see...")); + show_accept(vformat(TTR("Error while loading '%s'."), p_file.get_file()), TTR("OK")); } break; } } @@ -946,8 +949,6 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { int x = (img->get_width() - vp_size) / 2; int y = (img->get_height() - vp_size) / 2; - img->convert(Image::FORMAT_RGB8); - if (vp_size < preview_size) { // just square it. img->crop_from_point(x, y, vp_size, vp_size); @@ -962,6 +963,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { // We could get better pictures with better filters img->resize(preview_size, preview_size, Image::INTERPOLATE_CUBIC); } + img->convert(Image::FORMAT_RGB8); img->flip_y(); @@ -989,7 +991,7 @@ void EditorNode::_save_scene(String p_file, int idx) { if (!scene) { - show_accept(TTR("This operation can't be done without a tree root."), TTR("I see...")); + show_accept(TTR("This operation can't be done without a tree root."), TTR("OK")); return; } @@ -1017,7 +1019,7 @@ void EditorNode::_save_scene(String p_file, int idx) { if (err != OK) { - show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("I see...")); + show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK")); return; } @@ -1025,7 +1027,7 @@ void EditorNode::_save_scene(String p_file, int idx) { // (hacky but needed for the tree to update properly) Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!dummy_scene) { - show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("I see...")); + show_accept(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."), TTR("OK")); return; } memdelete(dummy_scene); @@ -1187,7 +1189,7 @@ void EditorNode::_dialog_action(String p_file) { ml = ResourceLoader::load(p_file, "MeshLibrary"); if (ml.is_null()) { - show_accept(TTR("Can't load MeshLibrary for merging!"), TTR("I see...")); + show_accept(TTR("Can't load MeshLibrary for merging!"), TTR("OK")); return; } } @@ -1200,7 +1202,7 @@ void EditorNode::_dialog_action(String p_file) { Error err = ResourceSaver::save(p_file, ml); if (err) { - show_accept(TTR("Error saving MeshLibrary!"), TTR("I see...")); + show_accept(TTR("Error saving MeshLibrary!"), TTR("OK")); return; } @@ -1212,7 +1214,7 @@ void EditorNode::_dialog_action(String p_file) { tileset = ResourceLoader::load(p_file, "TileSet"); if (tileset.is_null()) { - show_accept(TTR("Can't load TileSet for merging!"), TTR("I see...")); + show_accept(TTR("Can't load TileSet for merging!"), TTR("OK")); return; } @@ -1225,7 +1227,7 @@ void EditorNode::_dialog_action(String p_file) { Error err = ResourceSaver::save(p_file, tileset); if (err) { - show_accept("Error saving TileSet!", "I see..."); + show_accept(TTR("Error saving TileSet!"), TTR("OK")); return; } } break; @@ -1502,7 +1504,7 @@ void EditorNode::_edit_current() { if (main_plugin) { // special case if use of external editor is true - if (main_plugin->get_name() == "Script" && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) { + if (main_plugin->get_name() == "Script" && current_obj->get_class_name() != StringName("VisualScript") && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) { if (!changing_scene) main_plugin->edit(current_obj); } @@ -1579,7 +1581,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { Node *scene = editor_data.get_edited_scene_root(); if (!scene) { - show_accept(TTR("There is no defined scene to run."), TTR("I see...")); + show_accept(TTR("There is no defined scene to run."), TTR("OK")); return; } @@ -1633,7 +1635,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { if (scene->get_filename() == "") { - show_accept(TTR("Current scene was never saved, please save it prior to running."), TTR("I see...")); + show_accept(TTR("Current scene was never saved, please save it prior to running."), TTR("OK")); return; } @@ -1664,7 +1666,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { if (error != OK) { - show_accept(TTR("Could not start subprocess!"), TTR("I see...")); + show_accept(TTR("Could not start subprocess!"), TTR("OK")); return; } @@ -1753,6 +1755,11 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } else { tab_closing = editor_data.get_edited_scene(); } + if (!editor_data.get_edited_scene_root(tab_closing)) { + // empty tab + _scene_tab_closed(tab_closing); + break; + } } // fallthrough case SCENE_TAB_CLOSE: @@ -1782,7 +1789,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (!scene) { - show_accept(TTR("This operation can't be done without a tree root."), TTR("I see...")); + show_accept(TTR("This operation can't be done without a tree root."), TTR("OK")); break; } @@ -1845,7 +1852,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a scene."), TTR("I see...")); + show_accept(TTR("This operation can't be done without a scene."), TTR("OK")); break; } @@ -1865,7 +1872,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { //Make sure that the scene has a root before trying to convert to tileset if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a root node."), TTR("I see...")); + show_accept(TTR("This operation can't be done without a root node."), TTR("OK")); break; } @@ -1886,7 +1893,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a selected node."), TTR("I see...")); + show_accept(TTR("This operation can't be done without a selected node."), TTR("OK")); break; } @@ -2047,6 +2054,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { project_settings->popup_project_settings(); } break; + case RUN_PROJECT_DATA_FOLDER: { + + OS::get_singleton()->shell_open(OS::get_singleton()->get_user_data_dir()); + } break; case FILE_QUIT: case RUN_PROJECT_MANAGER: { @@ -2159,7 +2170,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { OS::get_singleton()->set_low_processor_usage_mode(false); EditorSettings::get_singleton()->set_project_metadata("editor_options", "update_always", true); - show_accept(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."), TTR("I see...")); + show_accept(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."), TTR("OK")); } break; case SETTINGS_UPDATE_CHANGES: { @@ -2179,6 +2190,14 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { settings_config_dialog->popup_edit_settings(); } break; + case SETTINGS_EDITOR_DATA_FOLDER: { + + OS::get_singleton()->shell_open(EditorSettings::get_singleton()->get_data_dir()); + } break; + case SETTINGS_EDITOR_CONFIG_FOLDER: { + + OS::get_singleton()->shell_open(EditorSettings::get_singleton()->get_settings_dir()); + } break; case SETTINGS_MANAGE_EXPORT_TEMPLATES: { export_template_manager->popup_manager(); @@ -2765,7 +2784,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b if (!lpath.begins_with("res://")) { - show_accept(TTR("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."), TTR("Ugh")); + show_accept(TTR("Error loading scene, it must be inside the project path. Use 'Import' to open the scene, then save it inside the project path."), TTR("OK")); opening_prev = false; return ERR_FILE_NOT_FOUND; } @@ -3833,6 +3852,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { + if (scene_tabs->get_hovered_tab() >= 0) { if (mb->get_button_index() == BUTTON_MIDDLE && mb->is_pressed()) { _scene_tab_closed(scene_tabs->get_hovered_tab()); @@ -3842,6 +3862,26 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { _menu_option_confirm(FILE_NEW_SCENE, true); } } + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { + + // context menu + scene_tabs_context_menu->clear(); + scene_tabs_context_menu->set_size(Size2(1, 1)); + + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/new_scene"), FILE_NEW_SCENE); + if (scene_tabs->get_hovered_tab() >= 0) { + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene"), FILE_SAVE_SCENE); + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_scene_as"), FILE_SAVE_AS_SCENE); + } + scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); + if (scene_tabs->get_hovered_tab() >= 0) { + scene_tabs_context_menu->add_separator(); + scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE); + scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE); + } + scene_tabs_context_menu->set_position(mb->get_global_position()); + scene_tabs_context_menu->popup(); + } } } @@ -3935,7 +3975,7 @@ void EditorNode::raise_bottom_panel_item(Control *p_item) { if (bottom_panel_items[i].control == p_item) { bottom_panel_items[i].button->raise(); - SWAP(bottom_panel_items[i], bottom_panel_items[bottom_panel_items.size() - 1]); + SWAP(bottom_panel_items.write[i], bottom_panel_items.write[bottom_panel_items.size() - 1]); break; } } @@ -4627,6 +4667,20 @@ EditorNode::EditorNode() { import_texture.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_texture); + Ref<ResourceImporterLayeredTexture> import_3d; + import_3d.instance(); + import_3d->set_3d(true); + ResourceFormatImporter::get_singleton()->add_importer(import_3d); + + Ref<ResourceImporterLayeredTexture> import_array; + import_array.instance(); + import_array->set_3d(false); + ResourceFormatImporter::get_singleton()->add_importer(import_array); + + Ref<ResourceImporterImage> import_image; + import_image.instance(); + ResourceFormatImporter::get_singleton()->add_importer(import_image); + Ref<ResourceImporterCSVTranslation> import_csv_translation; import_csv_translation.instance(); ResourceFormatImporter::get_singleton()->add_importer(import_csv_translation); @@ -4719,6 +4773,8 @@ EditorNode::EditorNode() { EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true); EDITOR_DEF_RST("interface/inspector/capitalize_properties", true); EDITOR_DEF_RST("interface/inspector/disable_folding", false); + EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); + EDITOR_DEF("interface/inspector/horizontal_vector3_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial"); EDITOR_DEF("run/auto_save/save_before_running", true); @@ -4897,6 +4953,7 @@ EditorNode::EditorNode() { scene_tabs = memnew(Tabs); scene_tabs->add_style_override("tab_fg", gui_base->get_stylebox("SceneTabFG", "EditorStyles")); scene_tabs->add_style_override("tab_bg", gui_base->get_stylebox("SceneTabBG", "EditorStyles")); + scene_tabs->set_select_with_rmb(true); scene_tabs->add_tab("unsaved"); scene_tabs->set_tab_align(Tabs::ALIGN_LEFT); scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY)); @@ -4914,6 +4971,11 @@ EditorNode::EditorNode() { tabbar_container = memnew(HBoxContainer); scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); + scene_tabs_context_menu = memnew(PopupMenu); + tabbar_container->add_child(scene_tabs_context_menu); + scene_tabs_context_menu->connect("id_pressed", this, "_menu_option"); + scene_tabs_context_menu->set_hide_on_window_lose_focus(true); + srt->add_child(tabbar_container); tabbar_container->add_child(scene_tabs); distraction_free = memnew(ToolButton); @@ -5020,6 +5082,7 @@ EditorNode::EditorNode() { file_menu->set_tooltip(TTR("Operations with scene files.")); p = file_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE); p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE); p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE); @@ -5065,6 +5128,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(project_menu); p = project_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_item(TTR("Project Settings"), RUN_SETTINGS); p->add_separator(); p->connect("id_pressed", this, "_menu_option"); @@ -5078,6 +5142,9 @@ EditorNode::EditorNode() { tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES); p->add_separator(); + p->add_item(TTR("Open Project Data Folder"), RUN_PROJECT_DATA_FOLDER); + p->add_separator(); + #ifdef OSX_ENABLED p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q); #else @@ -5097,6 +5164,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(debug_menu); p = debug_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->set_hide_on_item_selection(false); p->add_check_item(TTR("Deploy with Remote Debug"), RUN_DEPLOY_REMOTE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When exporting or deploying, the resulting executable will attempt to connect to the IP of this computer in order to be debugged.")); @@ -5121,10 +5189,12 @@ EditorNode::EditorNode() { settings_menu->set_text(TTR("Editor")); settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); left_menu_hb->add_child(settings_menu); - p = settings_menu->get_popup(); + p = settings_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->add_item(TTR("Editor Settings"), SETTINGS_PREFERENCES); p->add_separator(); + editor_layouts = memnew(PopupMenu); editor_layouts->set_name("Layouts"); p->add_child(editor_layouts); @@ -5136,6 +5206,17 @@ EditorNode::EditorNode() { p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN); #endif p->add_separator(); + + if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) { + // Configuration and data folders are located in the same place (Windows/macOS) + p->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER); + } else { + // Separate configuration and data folders (Linux) + p->add_item(TTR("Open Editor Data Folder"), SETTINGS_EDITOR_DATA_FOLDER); + p->add_item(TTR("Open Editor Settings Folder"), SETTINGS_EDITOR_CONFIG_FOLDER); + } + p->add_separator(); + p->add_item(TTR("Manage Export Templates"), SETTINGS_MANAGE_EXPORT_TEMPLATES); // Help Menu @@ -5146,6 +5227,7 @@ EditorNode::EditorNode() { left_menu_hb->add_child(help_menu); p = help_menu->get_popup(); + p->set_hide_on_window_lose_focus(true); p->connect("id_pressed", this, "_menu_option"); p->add_icon_item(gui_base->get_icon("ClassList", "EditorIcons"), TTR("Classes"), HELP_CLASSES); p->add_icon_item(gui_base->get_icon("HelpSearch", "EditorIcons"), TTR("Search"), HELP_SEARCH); @@ -5469,6 +5551,7 @@ EditorNode::EditorNode() { EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor(); ScriptTextEditor::register_editor(); //register one for text scripts + TextEditor::register_editor(); if (StreamPeerSSL::is_available()) { add_editor_plugin(memnew(AssetLibraryEditorPlugin(this))); @@ -5527,6 +5610,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(SkeletonEditorPlugin(this))); + add_editor_plugin(memnew(SkeletonIKEditorPlugin(this))); add_editor_plugin(memnew(PhysicalBonePlugin(this))); // FIXME: Disabled as (according to reduz) users were complaining that it gets in the way diff --git a/editor/editor_node.h b/editor/editor_node.h index 38e68b2e09..85aa37ec7e 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -154,6 +154,7 @@ private: RUN_PLAY_CUSTOM_SCENE, RUN_SCENE_SETTINGS, RUN_SETTINGS, + RUN_PROJECT_DATA_FOLDER, RUN_PROJECT_MANAGER, RUN_FILE_SERVER, RUN_LIVE_DEBUG, @@ -168,6 +169,8 @@ private: SETTINGS_LAYOUT_SAVE, SETTINGS_LAYOUT_DELETE, SETTINGS_LAYOUT_DEFAULT, + SETTINGS_EDITOR_DATA_FOLDER, + SETTINGS_EDITOR_CONFIG_FOLDER, SETTINGS_MANAGE_EXPORT_TEMPLATES, SETTINGS_PICK_MAIN_SCENE, SETTINGS_TOGGLE_FULLSCREEN, @@ -220,6 +223,7 @@ private: //main tabs Tabs *scene_tabs; + PopupMenu *scene_tabs_context_menu; Panel *tab_preview_panel; TextureRect *tab_preview; int tab_closing; diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 843267d673..a926401558 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -479,15 +479,6 @@ void EditorPlugin::notify_resource_saved(const Ref<Resource> &p_resource) { emit_signal("resource_saved", p_resource); } -Ref<SpatialEditorGizmo> EditorPlugin::create_spatial_gizmo(Spatial *p_spatial) { - //?? - if (get_script_instance() && get_script_instance()->has_method("create_spatial_gizmo")) { - return get_script_instance()->call("create_spatial_gizmo", p_spatial); - } - - return Ref<SpatialEditorGizmo>(); -} - bool EditorPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { if (get_script_instance() && get_script_instance()->has_method("forward_canvas_gui_input")) { @@ -765,10 +756,6 @@ void EditorPlugin::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); - MethodInfo gizmo = MethodInfo(Variant::OBJECT, "create_spatial_gizmo", PropertyInfo(Variant::OBJECT, "for_spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial")); - gizmo.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; - gizmo.return_val.hint_string = "EditorSpatialGizmo"; - ClassDB::add_virtual_method(get_class_static(), gizmo); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_plugin_name")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::OBJECT, "get_plugin_icon")); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "has_main_screen")); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 72e21b2f7f..c417f487dc 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -51,7 +51,6 @@ class Camera; class EditorSelection; class EditorExport; class EditorSettings; -class SpatialEditorGizmo; class EditorImportPlugin; class EditorExportPlugin; class EditorResourcePreview; @@ -171,7 +170,6 @@ public: void notify_scene_closed(const String &scene_filepath); void notify_resource_saved(const Ref<Resource> &p_resource); - virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); virtual void forward_draw_over_viewport(Control *p_overlay); virtual void forward_force_draw_over_viewport(Control *p_overlay); diff --git a/editor/editor_profiler.cpp b/editor/editor_profiler.cpp index d4a97b7095..67700b59de 100644 --- a/editor/editor_profiler.cpp +++ b/editor/editor_profiler.cpp @@ -37,9 +37,9 @@ void EditorProfiler::_make_metric_ptrs(Metric &m) { for (int i = 0; i < m.categories.size(); i++) { - m.category_ptrs[m.categories[i].signature] = &m.categories[i]; + m.category_ptrs[m.categories[i].signature] = &m.categories.write[i]; for (int j = 0; j < m.categories[i].items.size(); j++) { - m.item_ptrs[m.categories[i].items[j].signature] = &m.categories[i].items[j]; + m.item_ptrs[m.categories[i].items[j].signature] = &m.categories.write[i].items.write[j]; } } } @@ -50,8 +50,8 @@ void EditorProfiler::add_frame_metric(const Metric &p_metric, bool p_final) { if (last_metric >= frame_metrics.size()) last_metric = 0; - frame_metrics[last_metric] = p_metric; - _make_metric_ptrs(frame_metrics[last_metric]); + frame_metrics.write[last_metric] = p_metric; + _make_metric_ptrs(frame_metrics.write[last_metric]); updating_frame = true; cursor_metric_edit->set_max(frame_metrics[last_metric].frame_number); @@ -108,7 +108,7 @@ static String _get_percent_txt(float p_value, float p_total) { return String::num((p_value / p_total) * 100, 1) + "%"; } -String EditorProfiler::_get_time_as_text(Metric &m, float p_time, int p_calls) { +String EditorProfiler::_get_time_as_text(const Metric &m, float p_time, int p_calls) { int dmode = display_mode->get_selected(); @@ -192,18 +192,18 @@ void EditorProfiler::_update_plot() { float highest = 0; for (int i = 0; i < frame_metrics.size(); i++) { - Metric &m = frame_metrics[i]; + const Metric &m = frame_metrics[i]; if (!m.valid) continue; for (Set<StringName>::Element *E = plot_sigs.front(); E; E = E->next()) { - Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); + const Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); if (F) { highest = MAX(F->get()->total_time, highest); } - Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); + const Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); if (G) { if (use_self) { highest = MAX(G->get()->self, highest); @@ -256,18 +256,18 @@ void EditorProfiler::_update_plot() { } //get - Metric &m = frame_metrics[idx]; + const Metric &m = frame_metrics[idx]; if (m.valid == false) continue; //skip because invalid float value = 0; - Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); + const Map<StringName, Metric::Category *>::Element *F = m.category_ptrs.find(E->get()); if (F) { value = F->get()->total_time; } - Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); + const Map<StringName, Metric::Category::Item *>::Element *G = m.item_ptrs.find(E->get()); if (G) { if (use_self) { value = G->get()->self; @@ -375,7 +375,7 @@ void EditorProfiler::_update_frame() { variables->clear(); TreeItem *root = variables->create_item(); - Metric &m = frame_metrics[cursor_metric]; + const Metric &m = frame_metrics[cursor_metric]; int dtime = display_time->get_selected(); @@ -394,7 +394,7 @@ void EditorProfiler::_update_frame() { } for (int j = 0; j < m.categories[i].items.size(); j++) { - Metric::Category::Item &it = m.categories[i].items[j]; + const Metric::Category::Item &it = m.categories[i].items[j]; TreeItem *item = variables->create_item(category); item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); diff --git a/editor/editor_profiler.h b/editor/editor_profiler.h index cb451475e7..d445ad58aa 100644 --- a/editor/editor_profiler.h +++ b/editor/editor_profiler.h @@ -136,7 +136,7 @@ private: void _activate_pressed(); void _clear_pressed(); - String _get_time_as_text(Metric &m, float p_time, int p_calls); + String _get_time_as_text(const Metric &m, float p_time, int p_calls); void _make_metric_ptrs(Metric &m); void _item_edited(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 0b49b5a801..83a3662f21 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -258,6 +258,51 @@ EditorPropertyPath::EditorPropertyPath() { global = false; } +///////////////////// CLASS NAME ///////////////////////// + +void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) { + + base_type = p_base_type; + dialog->set_base_type(base_type); + selected_type = p_selected_type; + property->set_text(selected_type); +} + +void EditorPropertyClassName::update_property() { + + String s = get_edited_object()->get(get_edited_property()); + property->set_text(s); + selected_type = s; +} + +void EditorPropertyClassName::_property_selected() { + dialog->popup_create(true); +} + +void EditorPropertyClassName::_dialog_created() { + selected_type = dialog->get_selected_type(); + emit_signal("property_changed", get_edited_property(), selected_type); + update_property(); +} + +void EditorPropertyClassName::_bind_methods() { + ClassDB::bind_method(D_METHOD("_dialog_created"), &EditorPropertyClassName::_dialog_created); + ClassDB::bind_method(D_METHOD("_property_selected"), &EditorPropertyClassName::_property_selected); +} + +EditorPropertyClassName::EditorPropertyClassName() { + property = memnew(Button); + property->set_clip_text(true); + add_child(property); + add_focusable(property); + property->set_text(selected_type); + property->connect("pressed", this, "_property_selected"); + dialog = memnew(CreateDialog); + dialog->set_base_type(base_type); + dialog->connect("create", this, "_dialog_created"); + add_child(dialog); +} + ///////////////////// MEMBER ///////////////////////// void EditorPropertyMember::_property_selected(const String &p_selected) { @@ -523,6 +568,7 @@ public: uint32_t value; Vector<Rect2> flag_rects; Vector<String> names; + Vector<String> tooltips; virtual Size2 get_minimum_size() const { Ref<Font> font = get_font("font", "Label"); @@ -531,8 +577,8 @@ public: virtual String get_tooltip(const Point2 &p_pos) const { for (int i = 0; i < flag_rects.size(); i++) { - if (i < names.size() && flag_rects[i].has_point(p_pos)) { - return names[i]; + if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) { + return tooltips[i]; } } return String(); @@ -636,6 +682,7 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) { } Vector<String> names; + Vector<String> tooltips; for (int i = 0; i < 20; i++) { String name; @@ -647,12 +694,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) { name = TTR("Layer") + " " + itos(i + 1); } - name += "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i); - names.push_back(name); + tooltips.push_back(name + "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i)); } grid->names = names; + grid->tooltips = tooltips; } void EditorPropertyLayers::_button_pressed() { @@ -681,6 +728,7 @@ void EditorPropertyLayers::_menu_pressed(int p_menu) { grid->value |= (1 << p_menu); } grid->update(); + layers->set_item_checked(layers->get_item_index(p_menu), grid->value & (1 << p_menu)); _grid_changed(grid->value); } @@ -706,6 +754,7 @@ EditorPropertyLayers::EditorPropertyLayers() { set_bottom_editor(hb); layers = memnew(PopupMenu); add_child(layers); + layers->set_hide_on_checkable_item_selection(false); layers->connect("id_pressed", this, "_menu_pressed"); } ///////////////////// INT ///////////////////////// @@ -1020,18 +1069,35 @@ void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector2::EditorPropertyVector2() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); + + BoxContainer *bc; + + if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + static const char *desc[2] = { "x", "y" }; for (int i = 0; i < 2; i++) { spin[i] = memnew(EditorSpinSlider); spin[i]->set_flat(true); spin[i]->set_label(desc[i]); - vb->add_child(spin[i]); + bc->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", this, "_value_changed"); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this } - set_label_reference(spin[0]); //show text and buttons around this setting = false; } @@ -1146,19 +1212,35 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector3::EditorPropertyVector3() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + + BoxContainer *bc; + + if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + static const char *desc[3] = { "x", "y", "z" }; for (int i = 0; i < 3; i++) { spin[i] = memnew(EditorSpinSlider); - spin[i]->set_label(desc[i]); spin[i]->set_flat(true); - - vb->add_child(spin[i]); + spin[i]->set_label(desc[i]); + bc->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", this, "_value_changed"); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this } - set_label_reference(spin[0]); //show text and buttons around this setting = false; } ///////////////////// PLANE ///////////////////////// @@ -1210,18 +1292,36 @@ void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool } EditorPropertyPlane::EditorPropertyPlane() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + + BoxContainer *bc; + + if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + static const char *desc[4] = { "x", "y", "z", "d" }; for (int i = 0; i < 4; i++) { spin[i] = memnew(EditorSpinSlider); - spin[i]->set_label(desc[i]); spin[i]->set_flat(true); - vb->add_child(spin[i]); + spin[i]->set_label(desc[i]); + bc->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", this, "_value_changed"); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this } - set_label_reference(spin[0]); //show text and buttons around this setting = false; } @@ -1274,19 +1374,35 @@ void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p } EditorPropertyQuat::EditorPropertyQuat() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + + BoxContainer *bc; + + if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + static const char *desc[4] = { "x", "y", "z", "w" }; for (int i = 0; i < 4; i++) { spin[i] = memnew(EditorSpinSlider); - spin[i]->set_label(desc[i]); spin[i]->set_flat(true); - - vb->add_child(spin[i]); + spin[i]->set_label(desc[i]); + bc->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", this, "_value_changed"); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this } - set_label_reference(spin[0]); //show text and buttons around this setting = false; } @@ -2607,6 +2723,10 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) { EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText); add_property_editor(p_path, editor); + } else if (p_hint == PROPERTY_HINT_TYPE_STRING) { + EditorPropertyClassName *editor = memnew(EditorPropertyClassName); + editor->setup("Object", p_hint_text); + add_property_editor(p_path, editor); } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector<String> extensions = p_hint_text.split(","); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 0afb1bf955..ccd73d2539 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -119,6 +119,25 @@ public: EditorPropertyPath(); }; +class EditorPropertyClassName : public EditorProperty { + GDCLASS(EditorPropertyClassName, EditorProperty) +private: + CreateDialog *dialog; + Button *property; + String selected_type; + String base_type; + void _property_selected(); + void _dialog_created(); + +protected: + static void _bind_methods(); + +public: + void setup(const String &p_base_type, const String &p_selected_type); + virtual void update_property(); + EditorPropertyClassName(); +}; + class EditorPropertyMember : public EditorProperty { GDCLASS(EditorPropertyMember, EditorProperty) public: diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index aa67ea03d7..a9eaad47b7 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -46,7 +46,7 @@ bool EditorResourcePreviewGenerator::handles(const String &p_type) const { ERR_EXPLAIN("EditorResourcePreviewGenerator::handles needs to be overridden"); ERR_FAIL_V(false); } -Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from) { +Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from) const { if (get_script_instance() && get_script_instance()->has_method("generate")) { return get_script_instance()->call("generate", p_from); @@ -55,7 +55,7 @@ Ref<Texture> EditorResourcePreviewGenerator::generate(const RES &p_from) { ERR_FAIL_V(Ref<Texture>()); } -Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path) { +Ref<Texture> EditorResourcePreviewGenerator::generate_from_path(const String &p_path) const { if (get_script_instance() && get_script_instance()->has_method("generate_from_path")) { return get_script_instance()->call("generate_from_path", p_path); diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index e2276aa11d..74841b1a1e 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -63,8 +63,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); - virtual Ref<Texture> generate_from_path(const String &p_path); + virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate_from_path(const String &p_path) const; EditorResourcePreviewGenerator(); }; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 466b12157d..a14ced1340 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -298,17 +298,17 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/editor/main_font_size", 14); hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "10,40,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/code_font_size", 14); - hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font_hinting", 2); - hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/code_font_hinting", 2); - hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font", ""); - hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/main_font_bold", ""); - hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/code_font", ""); - hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT); _initial_set("interface/editor/dim_editor_on_dialog_popup", true); _initial_set("interface/editor/dim_amount", 0.6f); hints["interface/editor/dim_amount"] = PropertyInfo(Variant::REAL, "interface/editor/dim_amount", PROPERTY_HINT_RANGE, "0,1,0.01", PROPERTY_USAGE_DEFAULT); @@ -396,7 +396,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/cursor/block_caret", false); _initial_set("text_editor/cursor/caret_blink", true); - _initial_set("text_editor/cursor/caret_blink_speed", 0.65); + _initial_set("text_editor/cursor/caret_blink_speed", 0.5); hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01"); _initial_set("text_editor/cursor/right_click_moves_caret", true); @@ -881,7 +881,7 @@ fail: Vector<String> list = extra_config->get_value("init_projects", "list"); for (int i = 0; i < list.size(); i++) { - list[i] = exe_path + "/" + list[i]; + list.write[i] = exe_path + "/" + list[i]; }; extra_config->set_value("init_projects", "list", list); }; diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 0e6d81d13b..b2c9f9865a 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -30,7 +30,13 @@ #include "editor_spin_slider.h" #include "editor_scale.h" +#include "math/expression.h" #include "os/input.h" + +String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const { + return rtos(get_value()); +} + String EditorSpinSlider::get_text_value() const { int zeros = Math::step_decimals(get_step()); return String::num(get_value(), zeros); @@ -270,10 +276,12 @@ void EditorSpinSlider::_notification(int p_what) { if (p_what == NOTIFICATION_FOCUS_ENTER) { /* Sorry, I dont like this, it makes navigating the different fields with arrows more difficult. * Just press enter to edit. - * if (!Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) { + * if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && !value_input_just_closed) { _focus_entered(); }*/ - + if ((Input::get_singleton()->is_action_pressed("ui_focus_next") || Input::get_singleton()->is_action_pressed("ui_focus_prev")) && !value_input_just_closed) { + _focus_entered(); + } value_input_just_closed = false; } } @@ -307,6 +315,21 @@ String EditorSpinSlider::get_label() const { return label; } +void EditorSpinSlider::_evaluate_input_text() { + String text = value_input->get_text(); + Ref<Expression> expr; + expr.instance(); + Error err = expr->parse(text); + if (err != OK) { + return; + } + + Variant v = expr->execute(Array(), NULL, false); + if (v.get_type() == Variant::NIL) + return; + set_value(v); +} + //text_entered signal void EditorSpinSlider::_value_input_entered(const String &p_text) { value_input_just_closed = true; @@ -315,13 +338,13 @@ void EditorSpinSlider::_value_input_entered(const String &p_text) { //modal_closed signal void EditorSpinSlider::_value_input_closed() { - set_value(value_input->get_text().to_double()); + _evaluate_input_text(); value_input_just_closed = true; } //focus_exited signal void EditorSpinSlider::_value_focus_exited() { - set_value(value_input->get_text().to_double()); + _evaluate_input_text(); // focus is not on the same element after the vlalue_input was exited // -> focus is on next element // -> TAB was pressed diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h index fb32534ef4..e48eb171b8 100644 --- a/editor/editor_spin_slider.h +++ b/editor/editor_spin_slider.h @@ -73,6 +73,8 @@ class EditorSpinSlider : public Range { bool use_custom_label_color; Color custom_label_color; + void _evaluate_input_text(); + protected: void _notification(int p_what); void _gui_input(const Ref<InputEvent> &p_event); @@ -82,6 +84,8 @@ protected: void _focus_entered(); public: + String get_tooltip(const Point2 &p_pos) const; + String get_text_value() const; void set_label(const String &p_label); String get_label() const; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index ea9f6db61a..76fc7fd0cd 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -237,8 +237,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = ImageLoaderSVG::set_convert_colors(NULL); clock_t end_time = clock(); - - double time_d = (double)(end_time - begin_time) / CLOCKS_PER_SEC; #else print_line("Sorry no icons for you"); #endif @@ -257,7 +255,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { String preset = EDITOR_DEF("interface/theme/preset", "Default"); - int icon_font_color_setting = EDITOR_DEF("interface/theme/icon_and_font_color", 0); bool highlight_tabs = EDITOR_DEF("interface/theme/highlight_tabs", false); int border_size = EDITOR_DEF("interface/theme/border_size", 1); @@ -370,6 +367,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Color success_color = accent_color.linear_interpolate(Color(0.2, 1, 0.2), 0.6) * 1.2; Color warning_color = accent_color.linear_interpolate(Color(1, 1, 0), 0.7) * 1.2; Color error_color = accent_color.linear_interpolate(Color(1, 0, 0), 0.8) * 1.7; + Color property_color = font_color.linear_interpolate(Color(0.5, 0.5, 0.5), 0.5); + if (!dark_theme) { // yellow on white themes is a P.I.T.A. warning_color = accent_color.linear_interpolate(Color(1, 0.8, 0), 0.9); @@ -380,6 +379,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("success_color", "Editor", success_color); theme->set_color("warning_color", "Editor", warning_color); theme->set_color("error_color", "Editor", error_color); + theme->set_color("property_color", "Editor", property_color); // 2d grid color const Color grid_minor_color = mono_color * Color(1.0, 1.0, 1.0, 0.07); @@ -828,6 +828,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_color", "LineEdit", font_color); theme->set_color("cursor_color", "LineEdit", font_color); theme->set_color("selection_color", "LineEdit", font_color_selection); + theme->set_color("clear_button_color", "LineEdit", font_color); + theme->set_color("clear_button_color_pressed", "LineEdit", accent_color); // TextEdit theme->set_stylebox("normal", "TextEdit", style_widget); diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp index a218070933..b9e0c7d0fa 100644 --- a/editor/fileserver/editor_file_server.cpp +++ b/editor/fileserver/editor_file_server.cpp @@ -78,7 +78,7 @@ void EditorFileServer::_subthread_start(void *s) { _close_client(cd); ERR_FAIL_COND(err != OK); } - passutf8[passlen] = 0; + passutf8.write[passlen] = 0; String s; s.parse_utf8(passutf8.ptr()); if (s != cd->efs->password) { @@ -145,7 +145,7 @@ void EditorFileServer::_subthread_start(void *s) { _close_client(cd); ERR_FAIL_COND(err != OK); } - fileutf8[namelen] = 0; + fileutf8.write[namelen] = 0; String s; s.parse_utf8(fileutf8.ptr()); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 2b89752c08..7b8a7afa87 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -196,7 +196,8 @@ void FileSystemDock::_notification(int p_what) { files->connect("item_activated", this, "_select_file"); button_hist_next->connect("pressed", this, "_fw_history"); button_hist_prev->connect("pressed", this, "_bw_history"); - search_box->add_icon_override("right_icon", get_icon("Search", ei)); + search_box->set_right_icon(get_icon("Search", ei)); + search_box->set_clear_button_enabled(true); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); @@ -252,7 +253,8 @@ void FileSystemDock::_notification(int p_what) { button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); - search_box->add_icon_override("right_icon", get_icon("Search", ei)); + search_box->set_right_icon(get_icon("Search", ei)); + search_box->set_clear_button_enabled(true); if (new_mode != display_mode) { set_display_mode(new_mode); @@ -1983,9 +1985,11 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { */ file_options = memnew(PopupMenu); + file_options->set_hide_on_window_lose_focus(true); add_child(file_options); folder_options = memnew(PopupMenu); + folder_options->set_hide_on_window_lose_focus(true); add_child(folder_options); split_box = memnew(VSplitContainer); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 004a49e2b4..2be1f1644e 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -143,7 +143,7 @@ void FindInFiles::_iterate() { // Scan folders first so we can build a list of files and have progress info later - PoolStringArray &folders_to_scan = _folders_stack[_folders_stack.size() - 1]; + PoolStringArray &folders_to_scan = _folders_stack.write[_folders_stack.size() - 1]; if (folders_to_scan.size() != 0) { // Scan one folder below diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index 2bfd2eb5c3..0efd14e932 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -287,8 +287,10 @@ void GroupDialog::_notification(int p_what) { add_button->set_icon(get_icon("Forward", "EditorIcons")); remove_button->set_icon(get_icon("Back", "EditorIcons")); - add_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - remove_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + add_filter->set_right_icon(get_icon("Search", "EditorIcons")); + add_filter->set_clear_button_enabled(true); + remove_filter->set_right_icon(get_icon("Search", "EditorIcons")); + remove_filter->set_clear_button_enabled(true); } break; } } diff --git a/editor/icons/SCsub b/editor/icons/SCsub index 7f94073e01..31bf8f116a 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -1,96 +1,14 @@ #!/usr/bin/env python Import('env') -from compat import StringIO +from platform_methods import run_in_subprocess +import editor_icons_builders -def make_editor_icons_action(target, source, env): - import os - - dst = target[0].srcnode().abspath - svg_icons = source - - icons_string = StringIO() - - for f in svg_icons: - - fname = str(f) - - icons_string.write('\t"') - - with open(fname, 'rb') as svgf: - b = svgf.read(1) - while(len(b) == 1): - icons_string.write("\\" + str(hex(ord(b)))[1:]) - b = svgf.read(1) - - - icons_string.write('"') - if fname != svg_icons[-1]: - icons_string.write(",") - icons_string.write('\n') - - s = StringIO() - s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - s.write("#ifndef _EDITOR_ICONS_H\n") - s.write("#define _EDITOR_ICONS_H\n") - s.write("static const int editor_icons_count = {};\n".format(len(svg_icons))) - s.write("static const char *editor_icons_sources[] = {\n") - s.write(icons_string.getvalue()) - s.write('};\n\n') - s.write("static const char *editor_icons_names[] = {\n") - - # this is used to store the indices of thumbnail icons - thumb_medium_indices = []; - thumb_big_indices = []; - index = 0 - for f in svg_icons: - - fname = str(f) - - icon_name = os.path.basename(fname)[5:-4].title().replace("_", "") - # some special cases - if icon_name in ['Int', 'Bool', 'Float']: - icon_name = icon_name.lower() - if icon_name.endswith("MediumThumb"): # don't know a better way to handle this - thumb_medium_indices.append(str(index)) - if icon_name.endswith("BigThumb"): # don't know a better way to handle this - thumb_big_indices.append(str(index)) - - s.write('\t"{0}"'.format(icon_name)) - - if fname != svg_icons[-1]: - s.write(",") - s.write('\n') - - index += 1 - - s.write('};\n') - - if thumb_medium_indices: - s.write("\n\n") - s.write("static const int editor_md_thumbs_count = {};\n".format(len(thumb_medium_indices))) - s.write("static const int editor_md_thumbs_indices[] = {") - s.write(", ".join(thumb_medium_indices)) - s.write("};\n") - if thumb_big_indices: - s.write("\n\n") - s.write("static const int editor_bg_thumbs_count = {};\n".format(len(thumb_big_indices))) - s.write("static const int editor_bg_thumbs_indices[] = {") - s.write(", ".join(thumb_big_indices)) - s.write("};\n") - - s.write("#endif\n") - - with open(dst, "w") as f: - f.write(s.getvalue()) - - s.close() - icons_string.close() - -make_editor_icons_builder = Builder(action=make_editor_icons_action, +make_editor_icons_builder = Builder(action=run_in_subprocess(editor_icons_builders.make_editor_icons_action), suffix='.h', src_suffix='.svg') + env['BUILDERS']['MakeEditorIconsBuilder'] = make_editor_icons_builder env.Alias('editor_icons', [env.MakeEditorIconsBuilder('#editor/editor_icons.gen.h', Glob("*.svg"))]) diff --git a/editor/icons/editor_icons_builders.py b/editor/icons/editor_icons_builders.py new file mode 100644 index 0000000000..dfd0802ce9 --- /dev/null +++ b/editor/icons/editor_icons_builders.py @@ -0,0 +1,96 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +from platform_methods import subprocess_main +from compat import StringIO + + +def make_editor_icons_action(target, source, env): + + dst = target[0] + svg_icons = source + + icons_string = StringIO() + + for f in svg_icons: + + fname = str(f) + + icons_string.write('\t"') + + with open(fname, 'rb') as svgf: + b = svgf.read(1) + while(len(b) == 1): + icons_string.write("\\" + str(hex(ord(b)))[1:]) + b = svgf.read(1) + + + icons_string.write('"') + if fname != svg_icons[-1]: + icons_string.write(",") + icons_string.write('\n') + + s = StringIO() + s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + s.write("#ifndef _EDITOR_ICONS_H\n") + s.write("#define _EDITOR_ICONS_H\n") + s.write("static const int editor_icons_count = {};\n".format(len(svg_icons))) + s.write("static const char *editor_icons_sources[] = {\n") + s.write(icons_string.getvalue()) + s.write('};\n\n') + s.write("static const char *editor_icons_names[] = {\n") + + # this is used to store the indices of thumbnail icons + thumb_medium_indices = []; + thumb_big_indices = []; + index = 0 + for f in svg_icons: + + fname = str(f) + + icon_name = os.path.basename(fname)[5:-4].title().replace("_", "") + # some special cases + if icon_name in ['Int', 'Bool', 'Float']: + icon_name = icon_name.lower() + if icon_name.endswith("MediumThumb"): # don't know a better way to handle this + thumb_medium_indices.append(str(index)) + if icon_name.endswith("BigThumb"): # don't know a better way to handle this + thumb_big_indices.append(str(index)) + + s.write('\t"{0}"'.format(icon_name)) + + if fname != svg_icons[-1]: + s.write(",") + s.write('\n') + + index += 1 + + s.write('};\n') + + if thumb_medium_indices: + s.write("\n\n") + s.write("static const int editor_md_thumbs_count = {};\n".format(len(thumb_medium_indices))) + s.write("static const int editor_md_thumbs_indices[] = {") + s.write(", ".join(thumb_medium_indices)) + s.write("};\n") + if thumb_big_indices: + s.write("\n\n") + s.write("static const int editor_bg_thumbs_count = {};\n".format(len(thumb_big_indices))) + s.write("static const int editor_bg_thumbs_indices[] = {") + s.write(", ".join(thumb_big_indices)) + s.write("};\n") + + s.write("#endif\n") + + with open(dst, "w") as f: + f.write(s.getvalue()) + + s.close() + icons_string.close() + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/editor/icons/icon_add_atlas_tile.svg b/editor/icons/icon_add_atlas_tile.svg new file mode 100644 index 0000000000..912a0ce2c9 --- /dev/null +++ b/editor/icons/icon_add_atlas_tile.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6h-2z" fill="#c9cfd4"/> +</svg> diff --git a/editor/icons/icon_add_autotile.svg b/editor/icons/icon_add_autotile.svg new file mode 100644 index 0000000000..2cc34d53b1 --- /dev/null +++ b/editor/icons/icon_add_autotile.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6h-2z" fill="#4490fc"/> +</svg> diff --git a/editor/icons/icon_add_single_tile.svg b/editor/icons/icon_add_single_tile.svg new file mode 100644 index 0000000000..01af8e0649 --- /dev/null +++ b/editor/icons/icon_add_single_tile.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6h-2z" fill="#fce844"/> +</svg> diff --git a/editor/icons/icon_information_sign.svg b/editor/icons/icon_information_sign.svg new file mode 100644 index 0000000000..95002b6948 --- /dev/null +++ b/editor/icons/icon_information_sign.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg8" + sodipodi:docname="icon_information_sign.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1047" + inkscape:window-height="603" + id="namedview10" + showgrid="false" + inkscape:zoom="14.521571" + inkscape:cx="12.730205" + inkscape:cy="8.6526495" + inkscape:window-x="654" + inkscape:window-y="156" + inkscape:window-maximized="0" + inkscape:current-layer="svg8" /> + <path + style="fill:#ffb65d;fill-opacity:1;fill-rule:evenodd;stroke-width:0.57024062" + inkscape:connector-curvature="0" + id="path2" + d="m 4.5291945,14.892249 h 6.8428865 l 3.421444,-3.421444 V 4.6279186 L 11.372081,1.2064749 H 4.5291945 L 1.1077509,4.6279186 v 6.8428864 z" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke-width:0.57024062" + id="rect829" + width="2.6243541" + height="6.5062103" + x="6.6998501" + y="6.3477535" /> + <ellipse + style="fill:#ffffff;fill-opacity:1;stroke-width:0.57024062" + id="path831" + cx="8.0393629" + cy="4.2154655" + rx="1.3941878" + ry="1.3668507" /> +</svg> diff --git a/editor/icons/icon_soft_body.svg b/editor/icons/icon_soft_body.svg new file mode 100644 index 0000000000..9930026b61 --- /dev/null +++ b/editor/icons/icon_soft_body.svg @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="icon_soft_body.svg"> + <metadata + id="metadata14"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs12" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="1027" + id="namedview10" + showgrid="false" + inkscape:zoom="18.792233" + inkscape:cx="2.8961304" + inkscape:cy="4.3816933" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /> + <path + style="opacity:1;fill:#fc9c9c;fill-opacity:0.99607843;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="m 2.3447105,1.6091897 c -0.011911,1.9816766 -1.4168958,3.9344766 0,5.9495986 1.4168957,2.0151221 0,6.6693597 0,6.6693597 l 10.9510055,0 c 0,0 1.780829,-4.4523824 0,-6.489075 -1.780829,-2.0366925 -0.183458,-4.119112 0,-6.1298833 z m 1.8894067,0.7549031 7.4390658,0 c -0.431995,1.5996085 -1.62289,4.0426807 0,5.3749802 1.622888,1.3322996 0,5.887932 0,5.887932 l -7.4390658,0 c 0,0 1.3903413,-4.3680495 0,-5.9780743 -1.3903412,-1.6100247 -0.3951213,-3.7149271 0,-5.2848379 z" + id="rect4142" + inkscape:connector-curvature="0" + sodipodi:nodetypes="czcczcccczcczc" /> +</svg> diff --git a/editor/icons/icon_texture_3_d.svg b/editor/icons/icon_texture_3_d.svg new file mode 100644 index 0000000000..dafdc8c68d --- /dev/null +++ b/editor/icons/icon_texture_3_d.svg @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_texture_3_d.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1853" + inkscape:window-height="1016" + id="namedview8" + showgrid="false" + inkscape:zoom="29.5" + inkscape:cx="15.226978" + inkscape:cy="9.4909723" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg6" /> + <g + id="g830" + transform="translate(0.35954582,-0.28763666)"> + <path + d="M 2,1 C 1.4477153,1 1,1.4477153 1,2 v 12 c 0,0.552285 0.4477153,1 1,1 h 12 c 0.552285,0 1,-0.447715 1,-1 V 2 C 15,1.4477153 14.552285,1 14,1 Z m 1,2 h 10 v 8 H 3 Z" + id="path2" + inkscape:connector-curvature="0" + style="fill:#e0e0e0;fill-opacity:0.99607999" + sodipodi:nodetypes="sssssssssccccc" /> + </g> + <g + aria-label="3D" + transform="scale(0.9167105,1.0908569)" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.12847996px;line-height:1.25;font-family:Ubuntu;-inkscape-font-specification:'Ubuntu Bold';letter-spacing:0px;word-spacing:0px;fill:#e0e0e0;fill-opacity:0.99607843;stroke:none;stroke-width:0.20321201" + id="text834"> + <path + d="m 5.8175194,8.9717502 q -0.2194689,0 -0.4633233,-0.032514 Q 5.1103417,8.9148508 4.8827442,8.86608 4.6551468,8.8173091 4.4681918,8.7604097 4.2812367,8.7035104 4.1755665,8.6547395 L 4.4112924,7.646808 q 0.2113405,0.089413 0.5364797,0.1950835 0.3332677,0.097542 0.8209765,0.097542 0.5608651,0 0.8209764,-0.2113404 0.2601114,-0.2113405 0.2601114,-0.5689936 0,-0.219469 -0.097542,-0.3657816 Q 6.6628814,6.6388764 6.5003118,6.5494632 6.3377422,6.4519214 6.1101447,6.4194075 5.8906758,6.3787651 5.6386929,6.3787651 H 5.167241 V 5.4033475 h 0.5364797 q 0.1788266,0 0.3413962,-0.032514 0.1706981,-0.032514 0.3007537,-0.1056703 0.1300557,-0.081285 0.203212,-0.2113404 0.081285,-0.1381842 0.081285,-0.3413962 0,-0.1544411 -0.065028,-0.2682398 Q 6.5003118,4.3303881 6.3946415,4.2572318 6.2970998,4.1840755 6.1589156,4.1515616 6.0288599,4.1109192 5.8906758,4.1109192 q -0.3495247,0 -0.6502784,0.1056702 Q 4.9477721,4.3222597 4.7039177,4.4767008 L 4.2731082,3.5906965 Q 4.4031639,3.5094117 4.573862,3.4199984 4.7526886,3.3305851 4.964029,3.2574288 5.1753695,3.1842725 5.4110954,3.1355016 q 0.2438544,-0.048771 0.5120943,-0.048771 0.4958373,0 0.8534904,0.1219272 0.3657816,0.1137987 0.6015075,0.3332677 0.2357259,0.2113405 0.3495246,0.5039657 0.1137987,0.2844968 0.1137987,0.625893 0,0.3332677 -0.186955,0.6502784 -0.186955,0.3088822 -0.5039657,0.4714518 0.4389379,0.1788266 0.6746638,0.5364797 0.2438544,0.3495246 0.2438544,0.8453619 0,0.3901671 -0.1300557,0.7234347 Q 7.808997,8.22393 7.5326287,8.4677844 7.2562604,8.7035104 6.825451,8.8416945 6.40277,8.9717502 5.8175194,8.9717502 Z" + style="fill:#e0e0e0;fill-opacity:0.99607843;stroke-width:0.20321201" + id="path836" /> + <path + d="m 10.502445,7.817506 q 0.08941,0.00813 0.203212,0.016257 0.121927,0 0.284497,0 0.951032,0 1.406227,-0.4795803 0.463323,-0.4795803 0.463323,-1.3249422 0,-0.8860044 -0.438938,-1.3411992 -0.438938,-0.4551949 -1.38997,-0.4551949 -0.130055,0 -0.26824,0.00813 -0.138184,0 -0.260111,0.016257 z M 14.16839,6.0292405 q 0,0.7315631 -0.227598,1.2761713 -0.227597,0.5446082 -0.650278,0.9022613 -0.414553,0.3576531 -1.01606,0.5364797 -0.601508,0.1788265 -1.349328,0.1788265 -0.341396,0 -0.796591,-0.032514 Q 9.6733402,8.86608 9.2344022,8.7766667 v -5.486724 q 0.438938,-0.081285 0.9103898,-0.1056702 0.47958,-0.032514 0.820976,-0.032514 0.723435,0 1.308686,0.1625696 0.593379,0.1625696 1.01606,0.5120943 0.422681,0.3495246 0.650278,0.8941328 0.227598,0.5446081 0.227598,1.3086853 z" + style="fill:#e0e0e0;fill-opacity:0.99607843;stroke-width:0.20321201" + id="path838" /> + </g> +</svg> diff --git a/editor/icons/icon_texture_array.svg b/editor/icons/icon_texture_array.svg new file mode 100644 index 0000000000..8297fc0f5d --- /dev/null +++ b/editor/icons/icon_texture_array.svg @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg6" + sodipodi:docname="icon_texture_array.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)"> + <metadata + id="metadata12"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1526" + inkscape:window-height="766" + id="namedview8" + showgrid="false" + inkscape:zoom="29.5" + inkscape:cx="8.3117238" + inkscape:cy="9.4909723" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <g + id="g830" + transform="translate(0.35954582,-0.28763666)"> + <path + d="M 2,1 C 1.4477153,1 1,1.4477153 1,2 v 12 c 0,0.552285 0.4477153,1 1,1 h 12 c 0.552285,0 1,-0.447715 1,-1 V 2 C 15,1.4477153 14.552285,1 14,1 Z m 1,2 h 10 v 8 H 3 Z" + id="path2" + inkscape:connector-curvature="0" + style="fill:#e0e0e0;fill-opacity:0.99607999" + sodipodi:nodetypes="sssssssssccccc" /> + </g> + <g + aria-label="[]" + transform="matrix(1.6197742,0,0,0.750929,-3.7231532,1.8329569)" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.29580784px;line-height:1.25;font-family:Ubuntu;-inkscape-font-specification:'Ubuntu Bold';letter-spacing:0px;word-spacing:0px;fill:#e0e0e0;fill-opacity:0.99607843;stroke:none;stroke-width:0.2073952" + id="text834"> + <path + d="M 4.7302951,2.4553483 H 6.978459 V 3.4425495 H 5.9082998 V 9.4984892 H 6.978459 V 10.48569 H 4.7302951 Z" + style="fill:#e0e0e0;fill-opacity:0.99607843;stroke-width:0.2073952" + id="path862" + inkscape:connector-curvature="0" /> + <path + d="M 10.138643,10.48569 H 7.8904794 V 9.4984892 H 8.9606386 V 3.4425495 H 7.8904794 V 2.4553483 h 2.2481636 z" + style="fill:#e0e0e0;fill-opacity:0.99607843;stroke-width:0.2073952" + id="path864" + inkscape:connector-curvature="0" /> + </g> +</svg> diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index a13f741ee7..22ea5883e8 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -579,12 +579,12 @@ static void _generate_tangents_and_binormals(const PoolVector<int> &p_indices, c .normalized(); } - tangents[index_arrayr[idx * 3 + 0]] += tangent; - binormals[index_arrayr[idx * 3 + 0]] += binormal; - tangents[index_arrayr[idx * 3 + 1]] += tangent; - binormals[index_arrayr[idx * 3 + 1]] += binormal; - tangents[index_arrayr[idx * 3 + 2]] += tangent; - binormals[index_arrayr[idx * 3 + 2]] += binormal; + tangents.write[index_arrayr[idx * 3 + 0]] += tangent; + binormals.write[index_arrayr[idx * 3 + 0]] += binormal; + tangents.write[index_arrayr[idx * 3 + 1]] += tangent; + binormals.write[index_arrayr[idx * 3 + 1]] += binormal; + tangents.write[index_arrayr[idx * 3 + 2]] += tangent; + binormals.write[index_arrayr[idx * 3 + 2]] += binormal; //print_line(itos(idx)+" tangent: "+tangent); //print_line(itos(idx)+" binormal: "+binormal); @@ -800,7 +800,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me total += weights[i].weight; if (total) for (int i = 0; i < weights.size(); i++) - weights[i].weight /= total; + weights.write[i].weight /= total; if (weights.size() == 0 || total == 0) { //if nothing, add a weight to bone 0 //no weights assigned @@ -987,7 +987,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me vertex_array.resize(vertex_set.size()); for (Set<Collada::Vertex>::Element *F = vertex_set.front(); F; F = F->next()) { - vertex_array[F->get().idx] = F->get(); + vertex_array.write[F->get().idx] = F->get(); } if (has_weights) { @@ -996,9 +996,9 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me Transform local_xform = p_local_xform; for (int i = 0; i < vertex_array.size(); i++) { - vertex_array[i].vertex = local_xform.xform(vertex_array[i].vertex); - vertex_array[i].normal = local_xform.basis.xform(vertex_array[i].normal).normalized(); - vertex_array[i].tangent.normal = local_xform.basis.xform(vertex_array[i].tangent.normal).normalized(); + vertex_array.write[i].vertex = local_xform.xform(vertex_array[i].vertex); + vertex_array.write[i].normal = local_xform.basis.xform(vertex_array[i].normal).normalized(); + vertex_array.write[i].tangent.normal = local_xform.basis.xform(vertex_array[i].tangent.normal).normalized(); if (local_xform_mirror) { //i shouldn't do this? wtf? //vertex_array[i].normal*=-1.0; @@ -1061,13 +1061,13 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me //float sum=0.0; for (int l = 0; l < VS::ARRAY_WEIGHTS_SIZE; l++) { if (l < vertex_array[k].weights.size()) { - weights[l] = vertex_array[k].weights[l].weight; - bones[l] = vertex_array[k].weights[l].bone_idx; + weights.write[l] = vertex_array[k].weights[l].weight; + bones.write[l] = vertex_array[k].weights[l].bone_idx; //sum += vertex_array[k].weights[l].weight; } else { - weights[l] = 0; - bones[l] = 0; + weights.write[l] = 0; + bones.write[l] = 0; } } @@ -1286,7 +1286,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres String str = joint_src->sarray[i]; ERR_FAIL_COND_V(!bone_remap_map.has(str), ERR_INVALID_DATA); - bone_remap[i] = bone_remap_map[str]; + bone_remap.write[i] = bone_remap_map[str]; } } @@ -1506,7 +1506,7 @@ void ColladaImport::_fix_param_animation_tracks() { const Vector<int> &rt = collada.state.referenced_tracks[track_name]; for (int rti = 0; rti < rt.size(); rti++) { - Collada::AnimationTrack *at = &collada.state.animation_tracks[rt[rti]]; + Collada::AnimationTrack *at = &collada.state.animation_tracks.write[rt[rti]]; at->target = E->key(); at->param = "morph/" + collada.state.mesh_name_map[mesh_name]; @@ -1540,7 +1540,7 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im for (int i = 0; i < collada.state.animation_tracks.size(); i++) { - Collada::AnimationTrack &at = collada.state.animation_tracks[i]; + const Collada::AnimationTrack &at = collada.state.animation_tracks[i]; //print_line("CHANNEL: "+at.target+" PARAM: "+at.param); String node; @@ -1698,7 +1698,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones if (nm.anim_tracks.size() == 1) { //use snapshot keys from anim track instead, because this was most likely exported baked - Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()]; + const Collada::AnimationTrack &at = collada.state.animation_tracks[nm.anim_tracks.front()->get()]; snapshots.clear(); for (int i = 0; i < at.keys.size(); i++) snapshots.push_back(at.keys[i].time); @@ -1723,7 +1723,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones found_anim = true; - Collada::AnimationTrack &at = collada.state.animation_tracks[ET->get()]; + const Collada::AnimationTrack &at = collada.state.animation_tracks[ET->get()]; int xform_idx = -1; for (int j = 0; j < cn->xform_list.size(); j++) { @@ -1745,18 +1745,18 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones Vector<float> data = at.get_value_at_time(snapshots[i]); ERR_CONTINUE(data.empty()); - Collada::Node::XForm &xf = cn->xform_list[xform_idx]; + Collada::Node::XForm &xf = cn->xform_list.write[xform_idx]; if (at.component == "ANGLE") { ERR_CONTINUE(data.size() != 1); ERR_CONTINUE(xf.op != Collada::Node::XForm::OP_ROTATE); ERR_CONTINUE(xf.data.size() < 4); - xf.data[3] = data[0]; + xf.data.write[3] = data[0]; } else if (at.component == "X" || at.component == "Y" || at.component == "Z") { int cn = at.component[0] - 'X'; ERR_CONTINUE(cn >= xf.data.size()); ERR_CONTINUE(data.size() > 1); - xf.data[cn] = data[0]; + xf.data.write[cn] = data[0]; } else if (data.size() == xf.data.size()) { xf.data = data; @@ -1862,7 +1862,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones continue; } - Collada::AnimationTrack &at = collada.state.animation_tracks[ti]; + const Collada::AnimationTrack &at = collada.state.animation_tracks[ti]; // take snapshots if (!collada.state.scene_map.has(at.target)) @@ -1965,7 +1965,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_ if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) { if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) { - state.animations[i]->set_loop(true); + state.animations.write[i]->set_loop(true); } } diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index eb0bc0f782..906d902b4a 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -203,7 +203,6 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { GLTFNode *node = memnew(GLTFNode); Dictionary n = nodes[i]; - print_line("node " + itos(i) + ": " + String(Variant(n))); if (n.has("name")) { node->name = n["name"]; } @@ -651,7 +650,7 @@ Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, int p } else { //fill with zeros, as bufferview is not defined. for (int i = 0; i < (a.count * component_count); i++) { - dst_buffer[i] = 0; + dst_buffer.write[i] = 0; } } @@ -794,7 +793,7 @@ Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, ret.resize(ret_size); { for (int i = 0; i < ret_size; i++) { - ret[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]); + ret.write[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]); } } return ret; @@ -808,8 +807,8 @@ Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFSta ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); ret.resize(attribs.size() / 4); for (int i = 0; i < ret.size(); i++) { - ret[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]); - ret[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]); + ret.write[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]); + ret.write[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]); } return ret; } @@ -823,9 +822,9 @@ Vector<Basis> EditorSceneImporterGLTF::_decode_accessor_as_basis(GLTFState &stat ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret); ret.resize(attribs.size() / 9); for (int i = 0; i < ret.size(); i++) { - ret[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2])); - ret[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5])); - ret[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8])); + ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2])); + ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5])); + ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8])); } return ret; } @@ -838,10 +837,10 @@ Vector<Transform> EditorSceneImporterGLTF::_decode_accessor_as_xform(GLTFState & ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret); ret.resize(attribs.size() / 16); for (int i = 0; i < ret.size(); i++) { - ret[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2])); - ret[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6])); - ret[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10])); - ret[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14])); + ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2])); + ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6])); + ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10])); + ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14])); } return ret; } @@ -1085,7 +1084,7 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { ERR_FAIL_COND_V(mesh.mesh->get_blend_shape_count() != weights.size(), ERR_PARSE_ERROR); mesh.blend_weights.resize(weights.size()); for (int j = 0; j < weights.size(); j++) { - mesh.blend_weights[j] = weights[j]; + mesh.blend_weights.write[j] = weights[j]; } } @@ -1139,7 +1138,7 @@ Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_b ERR_FAIL_INDEX_V(bvi, state.buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); - GLTFBufferView &bv = state.buffer_views[bvi]; + const GLTFBufferView &bv = state.buffer_views[bvi]; int bi = bv.buffer; ERR_FAIL_INDEX_V(bi, state.buffers.size(), ERR_PARAMETER_RANGE_ERROR); @@ -1596,7 +1595,7 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { PoolVector<float> weights = _decode_accessor_as_floats(state, output, false); ERR_FAIL_INDEX_V(state.nodes[node]->mesh, state.meshes.size(), ERR_PARSE_ERROR); - GLTFMesh *mesh = &state.meshes[state.nodes[node]->mesh]; + const GLTFMesh *mesh = &state.meshes[state.nodes[node]->mesh]; ERR_FAIL_COND_V(mesh->blend_weights.size() == 0, ERR_PARSE_ERROR); int wc = mesh->blend_weights.size(); @@ -1611,11 +1610,11 @@ Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { Vector<float> wdata; wdata.resize(wlen); for (int l = 0; l < wlen; l++) { - wdata[l] = r[l * wc + k]; + wdata.write[l] = r[l * wc + k]; } cf.values = wdata; - track->weight_tracks[k] = cf; + track->weight_tracks.write[k] = cf; } } else { WARN_PRINTS("Invalid path: " + path); @@ -1657,7 +1656,8 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node if (n->mesh >= 0) { ERR_FAIL_INDEX(n->mesh, state.meshes.size()); MeshInstance *mi = memnew(MeshInstance); - GLTFMesh &mesh = state.meshes[n->mesh]; + print_line("**creating mesh for: " + n->name); + GLTFMesh &mesh = state.meshes.write[n->mesh]; mi->set_mesh(mesh.mesh); if (mesh.mesh->get_name() == "") { mesh.mesh->set_name(n->name); @@ -1686,20 +1686,22 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node node->set_name(n->name); - p_parent->add_child(node); - node->set_owner(p_owner); - node->set_transform(n->xform); - n->godot_nodes.push_back(node); if (n->skin >= 0 && Object::cast_to<MeshInstance>(node)) { MeshInstance *mi = Object::cast_to<MeshInstance>(node); - //move skeleton around and place it on node, as the node _is_ a skeleton. + Skeleton *s = skeletons[n->skin]; - state.paths_to_skeleton[mi] = s; - //move it later, as skeleton may be moved around first + s->add_child(node); //According to spec, mesh should actually act as a child of the skeleton, as it inherits its transform + mi->set_skeleton_path(String("..")); + + } else { + p_parent->add_child(node); + node->set_transform(n->xform); } + node->set_owner(p_owner); + #if 0 for (int i = 0; i < n->skeleton_children.size(); i++) { @@ -1711,14 +1713,14 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node #endif for (int i = 0; i < n->children.size(); i++) { if (state.nodes[n->children[i]]->joints.size()) { - _generate_bone(state, n->children[i], skeletons, Vector<int>(), node); + _generate_bone(state, n->children[i], skeletons, node); } else { _generate_node(state, n->children[i], node, p_owner, skeletons); } } } -void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones, Node *p_parent_node) { +void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node) { ERR_FAIL_INDEX(p_node, state.nodes.size()); if (state.skeleton_nodes.has(p_node)) { @@ -1729,34 +1731,34 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto skeletons[i]->get_parent()->remove_child(skeletons[i]); p_parent_node->add_child(skeletons[i]); skeletons[i]->set_owner(owner); + //may have meshes as children, set owner in them too + for (int j = 0; j < skeletons[i]->get_child_count(); j++) { + skeletons[i]->get_child(j)->set_owner(owner); + } } } GLTFNode *n = state.nodes[p_node]; - Vector<int> parent_bones; for (int i = 0; i < n->joints.size(); i++) { - ERR_FAIL_COND(n->joints[i].skin < 0); - - int bone_index = n->joints[i].bone; + const int skin = n->joints[i].skin; + ERR_FAIL_COND(skin < 0); - Skeleton *s = skeletons[n->joints[i].skin]; - while (s->get_bone_count() <= bone_index) { - s->add_bone("Bone " + itos(s->get_bone_count())); - } + Skeleton *s = skeletons[skin]; + const GLTFNode *gltf_bone_node = state.nodes[state.skins[skin].bones[n->joints[i].bone].node]; + const String bone_name = gltf_bone_node->name; + const int parent = gltf_bone_node->parent; + const int parent_index = s->find_bone(state.nodes[parent]->name); - if (p_parent_bones.size()) { - s->set_bone_parent(bone_index, p_parent_bones[i]); - } - s->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse()); + const int bone_index = s->find_bone(bone_name); + s->set_bone_parent(bone_index, parent_index); n->godot_nodes.push_back(s); - n->joints[i].godot_bone_index = bone_index; - parent_bones.push_back(bone_index); + n->joints.write[i].godot_bone_index = bone_index; } for (int i = 0; i < n->children.size(); i++) { - _generate_bone(state, n->children[i], skeletons, parent_bones, p_parent_node); + _generate_bone(state, n->children[i], skeletons, p_parent_node); } } @@ -1981,8 +1983,9 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye if (node->joints.size()) { Transform xform; - xform.basis = Basis(rot); - xform.basis.scale(scale); + //xform.basis = Basis(rot); + //xform.basis.scale(scale); + xform.basis.set_quat_scale(rot, scale); xform.origin = pos; Skeleton *skeleton = skeletons[node->joints[i].skin]; @@ -2063,6 +2066,10 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_f if (name == "") { name = _gen_unique_name(state, "Skeleton"); } + for (int j = 0; j < state.skins[i].bones.size(); j++) { + s->add_bone(state.nodes[state.skins[i].bones[j].node]->name); + s->set_bone_rest(j, state.skins[i].bones[j].inverse_bind.affine_inverse()); + } s->set_name(name); root->add_child(s); s->set_owner(root); @@ -2070,18 +2077,12 @@ Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_f } for (int i = 0; i < state.root_nodes.size(); i++) { if (state.nodes[state.root_nodes[i]]->joints.size()) { - _generate_bone(state, state.root_nodes[i], skeletons, Vector<int>(), root); + _generate_bone(state, state.root_nodes[i], skeletons, root); } else { _generate_node(state, state.root_nodes[i], root, root, skeletons); } } - for (Map<Node *, Skeleton *>::Element *E = state.paths_to_skeleton.front(); E; E = E->next()) { - MeshInstance *mi = Object::cast_to<MeshInstance>(E->key()); - ERR_CONTINUE(!mi); - mi->set_skeleton_path(mi->get_path_to(E->get())); - } - for (int i = 0; i < skeletons.size(); i++) { skeletons[i]->localize_rests(); } diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h index 088036ce75..8258ec41fd 100644 --- a/editor/import/editor_scene_importer_gltf.h +++ b/editor/import/editor_scene_importer_gltf.h @@ -275,7 +275,6 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<GLTFAnimation> animations; Map<int, Vector<int> > skeleton_nodes; - Map<Node *, Skeleton *> paths_to_skeleton; //Map<int, Vector<int> > skin_users; //cache skin users @@ -311,7 +310,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter { Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex); Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex); - void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, const Vector<int> &p_parent_bones, Node *p_parent_node); + void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node); void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons); void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons); diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index ec0500361d..cf850eef03 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -119,7 +119,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const if (key != "") { for (int i = 1; i < line.size(); i++) { - translations[i - 1]->add_message(key, line[i]); + translations.write[i - 1]->add_message(key, line[i]); } } diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp new file mode 100644 index 0000000000..b6a67c0cd3 --- /dev/null +++ b/editor/import/resource_importer_image.cpp @@ -0,0 +1,79 @@ +#include "resource_importer_image.h" + +#include "io/image_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/texture.h" + +String ResourceImporterImage::get_importer_name() const { + + return "image"; +} + +String ResourceImporterImage::get_visible_name() const { + + return "Image"; +} +void ResourceImporterImage::get_recognized_extensions(List<String> *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} + +String ResourceImporterImage::get_save_extension() const { + return "image"; +} + +String ResourceImporterImage::get_resource_type() const { + + return "Image"; +} + +bool ResourceImporterImage::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterImage::get_preset_count() const { + return 0; +} +String ResourceImporterImage::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterImage::get_import_options(List<ImportOption> *r_options, int p_preset) const { +} + +Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { + + FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); + if (!f) { + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + } + + size_t len = f->get_len(); + + Vector<uint8_t> data; + data.resize(len); + + f->get_buffer(data.ptrw(), len); + + memdelete(f); + + f = FileAccess::open(p_save_path + ".image", FileAccess::WRITE); + + //save the header GDIM + const uint8_t header[4] = { 'G', 'D', 'I', 'M' }; + f->store_buffer(header, 4); + //SAVE the extension (so it can be recognized by the loader later + f->store_pascal_string(p_source_file.get_extension().to_lower()); + //SAVE the actual image + f->store_buffer(data.ptr(), len); + + memdelete(f); + + return OK; +} + +ResourceImporterImage::ResourceImporterImage() { +} diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h new file mode 100644 index 0000000000..5aadd00a35 --- /dev/null +++ b/editor/import/resource_importer_image.h @@ -0,0 +1,27 @@ +#ifndef RESOURCE_IMPORTER_IMAGE_H +#define RESOURCE_IMPORTER_IMAGE_H + +#include "image.h" +#include "io/resource_import.h" + +class ResourceImporterImage : public ResourceImporter { + GDCLASS(ResourceImporterImage, ResourceImporter) +public: + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + + ResourceImporterImage(); +}; + +#endif // RESOURCE_IMPORTER_IMAGE_H diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp new file mode 100644 index 0000000000..2f958a6fdd --- /dev/null +++ b/editor/import/resource_importer_layered_texture.cpp @@ -0,0 +1,274 @@ +#include "resource_importer_layered_texture.h" + +#include "resource_importer_texture.h" + +#include "editor/editor_file_system.h" +#include "editor/editor_node.h" +#include "io/config_file.h" +#include "io/image_loader.h" +#include "scene/resources/texture.h" + +String ResourceImporterLayeredTexture::get_importer_name() const { + + return is_3d ? "texture_3d" : "texture_array"; +} + +String ResourceImporterLayeredTexture::get_visible_name() const { + + return is_3d ? "Texture3D" : "TextureArray"; +} +void ResourceImporterLayeredTexture::get_recognized_extensions(List<String> *p_extensions) const { + + ImageLoader::get_recognized_extensions(p_extensions); +} +String ResourceImporterLayeredTexture::get_save_extension() const { + return is_3d ? "tex3d" : "texarr"; +} + +String ResourceImporterLayeredTexture::get_resource_type() const { + + return is_3d ? "Texture3D" : "TextureArray"; +} + +bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterLayeredTexture::get_preset_count() const { + return 3; +} +String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { + + static const char *preset_names[] = { + "3D", + "2D", + "ColorCorrect" + }; + + return preset_names[p_idx]; +} + +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, "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/mipmaps"), p_preset == PRESET_COLOR_CORRECT ? 0 : 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/srgb", PROPERTY_HINT_ENUM, "Disable,Enable"), p_preset == PRESET_3D ? 1 : 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/horizontal", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 16 : 8)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/vertical", PROPERTY_HINT_RANGE, "1,256,1"), p_preset == PRESET_COLOR_CORRECT ? 1 : 8)); +} + +void ResourceImporterLayeredTexture::_save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags) { + + FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); + f->store_8('G'); + f->store_8('D'); + if (is_3d) { + f->store_8('3'); + } else { + f->store_8('A'); + } + f->store_8('T'); //godot streamable texture + + f->store_32(p_images[0]->get_width()); + f->store_32(p_images[0]->get_height()); + f->store_32(p_images.size()); //depth + f->store_32(p_texture_flags); + if (p_compress_mode != COMPRESS_VIDEO_RAM) { + //vram needs to do a first compression to tell what the format is, for the rest its ok + f->store_32(p_images[0]->get_format()); + f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + } + + if ((p_compress_mode == COMPRESS_LOSSLESS) && p_images[0]->get_format() > Image::FORMAT_RGBA8) { + p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy + } + + for (int i = 0; i < p_images.size(); i++) { + + switch (p_compress_mode) { + case COMPRESS_LOSSLESS: { + + Ref<Image> image = p_images[i]->duplicate(); + if (p_mipmaps) { + image->generate_mipmaps(); + } else { + image->clear_mipmaps(); + } + + int mmc = image->get_mipmap_count() + 1; + f->store_32(mmc); + + for (int i = 0; i < mmc; i++) { + + if (i > 0) { + image->shrink_x2(); + } + + PoolVector<uint8_t> data = Image::lossless_packer(image); + int data_len = data.size(); + f->store_32(data_len); + + PoolVector<uint8_t>::Read r = data.read(); + f->store_buffer(r.ptr(), data_len); + } + + } break; + case COMPRESS_VIDEO_RAM: { + + Ref<Image> image = p_images[i]->duplicate(); + image->generate_mipmaps(false); + + Image::CompressSource csource = Image::COMPRESS_SOURCE_LAYERED; + image->compress(p_vram_compression, csource, 0.7); + + if (i == 0) { + //hack so we can properly tell the format + f->store_32(image->get_format()); + f->store_32(p_compress_mode); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + } + + PoolVector<uint8_t> data = image->get_data(); + int dl = data.size(); + + PoolVector<uint8_t>::Read r = data.read(); + f->store_buffer(r.ptr(), dl); + } break; + case COMPRESS_UNCOMPRESSED: { + + Ref<Image> image = p_images[i]->duplicate(); + + if (p_mipmaps) { + image->generate_mipmaps(); + } else { + image->clear_mipmaps(); + } + + PoolVector<uint8_t> data = image->get_data(); + int dl = data.size(); + + PoolVector<uint8_t>::Read r = data.read(); + + f->store_buffer(r.ptr(), dl); + + } break; + } + } + + memdelete(f); +} + +Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { + + int compress_mode = p_options["compress/mode"]; + int repeat = p_options["flags/repeat"]; + bool filter = p_options["flags/filter"]; + bool mipmaps = p_options["flags/mipmaps"]; + int srgb = p_options["flags/srgb"]; + int hslices = p_options["slices/horizontal"]; + int vslices = p_options["slices/vertical"]; + + Ref<Image> image; + image.instance(); + Error err = ImageLoader::load_image(p_source_file, image, NULL, false, 1.0); + if (err != OK) + return err; + + int tex_flags = 0; + if (repeat > 0) + tex_flags |= Texture::FLAG_REPEAT; + if (repeat == 2) + tex_flags |= Texture::FLAG_MIRRORED_REPEAT; + if (filter) + tex_flags |= Texture::FLAG_FILTER; + if (mipmaps || compress_mode == COMPRESS_VIDEO_RAM) + tex_flags |= Texture::FLAG_MIPMAPS; + if (srgb == 1) + tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR; + + Vector<Ref<Image> > slices; + + int slice_w = image->get_width() / hslices; + int slice_h = image->get_height() / vslices; + + //optimize + if (compress_mode == COMPRESS_VIDEO_RAM) { + //if using video ram, optimize + if (srgb) { + //remove alpha if not needed, so compression is more efficient + if (image->get_format() == Image::FORMAT_RGBA8 && !image->detect_alpha()) { + image->convert(Image::FORMAT_RGB8); + } + } else { + image->optimize_channels(); + } + } + + for (int i = 0; i < vslices; i++) { + for (int j = 0; j < hslices; j++) { + int x = slice_w * j; + int y = slice_h * i; + Ref<Image> slice = image->get_rect(Rect2(x, y, slice_w, slice_h)); + ERR_CONTINUE(slice.is_null() || slice->empty()); + if (slice->get_width() != slice_w || slice->get_height() != slice_h) { + slice->resize(slice_w, slice_h); + } + slices.push_back(slice); + } + } + + String extension = get_save_extension(); + + if (compress_mode == COMPRESS_VIDEO_RAM) { + //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). + //Android, GLES 2.x + + bool ok_on_pc = false; + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc")) { + + _save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, Image::COMPRESS_S3TC, mipmaps, tex_flags); + r_platform_variants->push_back("s3tc"); + ok_on_pc = true; + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { + + _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, Image::COMPRESS_ETC2, mipmaps, tex_flags); + r_platform_variants->push_back("etc2"); + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { + _save_tex(slices, p_save_path + ".etc." + extension, compress_mode, Image::COMPRESS_ETC, mipmaps, tex_flags); + r_platform_variants->push_back("etc"); + } + + if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { + + _save_tex(slices, p_save_path + ".pvrtc." + extension, compress_mode, Image::COMPRESS_PVRTC4, mipmaps, tex_flags); + r_platform_variants->push_back("pvrtc"); + } + + if (!ok_on_pc) { + EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correcly on PC."); + } + } else { + //import normally + _save_tex(slices, p_save_path + "." + extension, compress_mode, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags); + } + + return OK; +} + +ResourceImporterLayeredTexture *ResourceImporterLayeredTexture::singleton = NULL; + +ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() { + + singleton = this; + is_3d = true; +} + +ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() { +} diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h new file mode 100644 index 0000000000..ec73b2624d --- /dev/null +++ b/editor/import/resource_importer_layered_texture.h @@ -0,0 +1,57 @@ +#ifndef RESOURCE_IMPORTER_LAYERED_TEXTURE_H +#define RESOURCE_IMPORTER_LAYERED_TEXTURE_H + +#include "image.h" +#include "io/resource_import.h" + +class StreamTexture; + +class ResourceImporterLayeredTexture : public ResourceImporter { + GDCLASS(ResourceImporterLayeredTexture, ResourceImporter) + + bool is_3d; + +protected: + static void _texture_reimport_srgb(const Ref<StreamTexture> &p_tex); + static void _texture_reimport_3d(const Ref<StreamTexture> &p_tex); + static void _texture_reimport_normal(const Ref<StreamTexture> &p_tex); + + static ResourceImporterLayeredTexture *singleton; + +public: + static ResourceImporterLayeredTexture *get_singleton() { return singleton; } + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + enum Preset { + PRESET_3D, + PRESET_2D, + PRESET_COLOR_CORRECT, + }; + + enum CompressMode { + COMPRESS_LOSSLESS, + COMPRESS_VIDEO_RAM, + COMPRESS_UNCOMPRESSED + }; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + void _save_tex(const Vector<Ref<Image> > &p_images, const String &p_to_path, int p_compress_mode, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags); + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + + void update_imports(); + + void set_3d(bool p_3d) { is_3d = p_3d; } + ResourceImporterLayeredTexture(); + ~ResourceImporterLayeredTexture(); +}; +#endif // RESOURCE_IMPORTER_LAYERED_TEXTURE_H diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index b8dd4a87b7..5babf6419c 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -224,6 +224,13 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p while (true) { String l = f->get_line().strip_edges(); + while (l.length() && l[l.length() - 1] == '\\') { + String add = f->get_line().strip_edges(); + l += add; + if (add == String()) { + break; + } + } if (l.begins_with("v ")) { //vertex @@ -264,10 +271,12 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh> > &r_meshes, bool p face[0] = v[1].split("/"); face[1] = v[2].split("/"); ERR_FAIL_COND_V(face[0].size() == 0, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(face[0].size() != face[1].size(), ERR_FILE_CORRUPT); for (int i = 2; i < v.size() - 1; i++) { face[2] = v[i + 1].split("/"); + ERR_FAIL_COND_V(face[0].size() != face[2].size(), ERR_FILE_CORRUPT); for (int j = 0; j < 3; j++) { diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index a5ad34f377..b5e3466b12 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -750,7 +750,7 @@ void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) { Vector<String> strings = p_text.split("\n"); for (int i = 0; i < strings.size(); i++) { - strings[i] = strings[i].strip_edges(); + strings.write[i] = strings[i].strip_edges(); } List<StringName> anim_names; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index debdeb1c4a..41f5a892eb 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -215,19 +215,19 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s for (int i = 0; i < frames * format_channels; i++) { // 8 bit samples are UNSIGNED - data[i] = int8_t(file->get_8() - 128) / 128.f; + data.write[i] = int8_t(file->get_8() - 128) / 128.f; } } else if (format_bits == 32 && compression_code == 3) { for (int i = 0; i < frames * format_channels; i++) { //32 bit IEEE Float - data[i] = file->get_float(); + data.write[i] = file->get_float(); } } else if (format_bits == 16) { for (int i = 0; i < frames * format_channels; i++) { //16 bit SIGNED - data[i] = int16_t(file->get_16()) / 32768.f; + data.write[i] = int16_t(file->get_16()) / 32768.f; } } else { for (int i = 0; i < frames * format_channels; i++) { @@ -241,7 +241,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } s <<= (32 - format_bits); - data[i] = (int32_t(s) >> 16) / 32768.f; + data.write[i] = (int32_t(s) >> 16) / 32768.f; } } @@ -335,7 +335,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s float res = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); - new_data[i * format_channels + c] = res; + new_data.write[i * format_channels + c] = res; // update position and always keep fractional part within ]0...1] // in order to avoid 32bit floating point precision errors @@ -374,7 +374,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s float mult = 1.0 / max; for (int i = 0; i < data.size(); i++) { - data[i] *= mult; + data.write[i] *= mult; } } } @@ -408,7 +408,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s Vector<float> new_data; new_data.resize((last - first + 1) * format_channels); for (int i = first * format_channels; i < (last + 1) * format_channels; i++) { - new_data[i - first * format_channels] = data[i]; + new_data.write[i - first * format_channels] = data[i]; } data = new_data; @@ -433,7 +433,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s Vector<float> new_data; new_data.resize(data.size() / 2); for (int i = 0; i < frames; i++) { - new_data[i] = (data[i * 2 + 0] + data[i * 2 + 1]) / 2.0; + new_data.write[i] = (data[i * 2 + 0] + data[i * 2 + 1]) / 2.0; } data = new_data; @@ -465,8 +465,8 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s right.resize(tframes); for (int i = 0; i < tframes; i++) { - left[i] = data[i * 2 + 0]; - right[i] = data[i * 2 + 1]; + left.write[i] = data[i * 2 + 0]; + right.write[i] = data[i * 2 + 1]; } PoolVector<uint8_t> bleft; @@ -526,119 +526,5 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s return OK; } -void ResourceImporterWAV::_compress_ima_adpcm(const Vector<float> &p_data, PoolVector<uint8_t> &dst_data) { - - /*p_sample_data->data = (void*)malloc(len); - xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/ - - static const int16_t _ima_adpcm_step_table[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - - static const int8_t _ima_adpcm_index_table[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - - int datalen = p_data.size(); - int datamax = datalen; - if (datalen & 1) - datalen++; - - dst_data.resize(datalen / 2 + 4); - PoolVector<uint8_t>::Write w = dst_data.write(); - - int i, step_idx = 0, prev = 0; - uint8_t *out = w.ptr(); - //int16_t xm_prev=0; - const float *in = p_data.ptr(); - - /* initial value is zero */ - *(out++) = 0; - *(out++) = 0; - /* Table index initial value */ - *(out++) = 0; - /* unused */ - *(out++) = 0; - - for (i = 0; i < datalen; i++) { - int step, diff, vpdiff, mask; - uint8_t nibble; - int16_t xm_sample; - - if (i >= datamax) - xm_sample = 0; - else { - - xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767); - /* - if (xm_sample==32767 || xm_sample==-32768) - printf("clippy!\n",xm_sample); - */ - } - - //xm_sample=xm_sample+xm_prev; - //xm_prev=xm_sample; - - diff = (int)xm_sample - prev; - - nibble = 0; - step = _ima_adpcm_step_table[step_idx]; - vpdiff = step >> 3; - if (diff < 0) { - nibble = 8; - diff = -diff; - } - mask = 4; - while (mask) { - - if (diff >= step) { - - nibble |= mask; - diff -= step; - vpdiff += step; - } - - step >>= 1; - mask >>= 1; - }; - - if (nibble & 8) - prev -= vpdiff; - else - prev += vpdiff; - - if (prev > 32767) { - //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); - prev = 32767; - } else if (prev < -32768) { - //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev); - prev = -32768; - } - - step_idx += _ima_adpcm_index_table[nibble]; - if (step_idx < 0) - step_idx = 0; - else if (step_idx > 88) - step_idx = 88; - - if (i & 1) { - *out |= nibble << 4; - out++; - } else { - *out = nibble; - } - /*dataptr[i]=prev>>8;*/ - } -} - ResourceImporterWAV::ResourceImporterWAV() { } diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h index cfce5a31ee..f78ab09e9b 100644 --- a/editor/import/resource_importer_wav.h +++ b/editor/import/resource_importer_wav.h @@ -48,7 +48,118 @@ public: virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; - void _compress_ima_adpcm(const Vector<float> &p_data, PoolVector<uint8_t> &dst_data); + static void _compress_ima_adpcm(const Vector<float> &p_data, PoolVector<uint8_t> &dst_data) { + /*p_sample_data->data = (void*)malloc(len); + xm_s8 *dataptr=(xm_s8*)p_sample_data->data;*/ + + static const int16_t _ima_adpcm_step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + static const int8_t _ima_adpcm_index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + int datalen = p_data.size(); + int datamax = datalen; + if (datalen & 1) + datalen++; + + dst_data.resize(datalen / 2 + 4); + PoolVector<uint8_t>::Write w = dst_data.write(); + + int i, step_idx = 0, prev = 0; + uint8_t *out = w.ptr(); + //int16_t xm_prev=0; + const float *in = p_data.ptr(); + + /* initial value is zero */ + *(out++) = 0; + *(out++) = 0; + /* Table index initial value */ + *(out++) = 0; + /* unused */ + *(out++) = 0; + + for (i = 0; i < datalen; i++) { + int step, diff, vpdiff, mask; + uint8_t nibble; + int16_t xm_sample; + + if (i >= datamax) + xm_sample = 0; + else { + + xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767); + /* + if (xm_sample==32767 || xm_sample==-32768) + printf("clippy!\n",xm_sample); + */ + } + + //xm_sample=xm_sample+xm_prev; + //xm_prev=xm_sample; + + diff = (int)xm_sample - prev; + + nibble = 0; + step = _ima_adpcm_step_table[step_idx]; + vpdiff = step >> 3; + if (diff < 0) { + nibble = 8; + diff = -diff; + } + mask = 4; + while (mask) { + + if (diff >= step) { + + nibble |= mask; + diff -= step; + vpdiff += step; + } + + step >>= 1; + mask >>= 1; + }; + + if (nibble & 8) + prev -= vpdiff; + else + prev += vpdiff; + + if (prev > 32767) { + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip up %i\n",i,xm_sample,prev,diff,vpdiff,prev); + prev = 32767; + } else if (prev < -32768) { + //printf("%i,xms %i, prev %i,diff %i, vpdiff %i, clip down %i\n",i,xm_sample,prev,diff,vpdiff,prev); + prev = -32768; + } + + step_idx += _ima_adpcm_index_table[nibble]; + if (step_idx < 0) + step_idx = 0; + else if (step_idx > 88) + step_idx = 88; + + if (i & 1) { + *out |= nibble << 4; + out++; + } else { + *out = nibble; + } + /*dataptr[i]=prev>>8;*/ + } + } virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 0d0b12c911..615e9df043 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -140,8 +140,9 @@ void InspectorDock::_load_resource(const String &p_type) { void InspectorDock::_resource_file_selected(String p_file) { RES res = ResourceLoader::load(p_file); + if (res.is_null()) { - warning_dialog->get_ok()->set_text("Ugh"); + warning_dialog->get_ok()->set_text(TTR("OK")); warning_dialog->set_text(TTR("Failed to load resource.")); return; }; @@ -528,7 +529,8 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { search = memnew(LineEdit); search->set_h_size_flags(Control::SIZE_EXPAND_FILL); search->set_placeholder(TTR("Filter properties")); - search->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search->set_right_icon(get_icon("Search", "EditorIcons")); + search->set_clear_button_enabled(true); add_child(search); warning = memnew(Button); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 5052b69e24..2d341cdd93 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -345,7 +345,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) Vector<Vector2> vertices = _get_polygon(edited_point.polygon); ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false); - vertices[edited_point.vertex] = edited_point.pos - _get_offset(edited_point.polygon); + vertices.write[edited_point.vertex] = edited_point.pos - _get_offset(edited_point.polygon); undo_redo->create_action(TTR("Edit Poly")); _action_set_polygon(edited_point.polygon, pre_move_edit, vertices); @@ -445,7 +445,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) Vector<Vector2> vertices = _get_polygon(edited_point.polygon); ERR_FAIL_INDEX_V(edited_point.vertex, vertices.size(), false); - vertices[edited_point.vertex] = cpoint - _get_offset(edited_point.polygon); + vertices.write[edited_point.vertex] = cpoint - _get_offset(edited_point.polygon); _set_polygon(edited_point.polygon, vertices); } diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 8d17062248..27df60f87a 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -414,7 +414,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s; point.y = s.height - point.y; - points[j] = point; + points.write[j] = point; } for (int j = 0; j < 3; j++) { diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 23eeef9f20..9ab5436de8 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1333,7 +1333,7 @@ void AnimationPlayerEditor::_allocate_onion_layers() { bool is_present = onion.differences_only && i == captures - 1; // Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport - onion.captures[i] = VS::get_singleton()->viewport_create(); + onion.captures.write[i] = VS::get_singleton()->viewport_create(); VS::get_singleton()->viewport_set_usage(onion.captures[i], VS::VIEWPORT_USAGE_2D); VS::get_singleton()->viewport_set_size(onion.captures[i], capture_size.width, capture_size.height); VS::get_singleton()->viewport_set_update_mode(onion.captures[i], VS::VIEWPORT_UPDATE_ALWAYS); @@ -1473,7 +1473,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { float pos = cpos + step_off * anim->get_step(); bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length()); - onion.captures_valid[cidx] = valid; + onion.captures_valid.write[cidx] = valid; if (valid) { player->seek(pos, true); get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 04bd5f0cec..ee450333c8 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -679,7 +679,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { Ref<StyleBox> sb = name == selected_node ? style_selected : style; int strsize = font->get_string_size(name).width; - NodeRect &nr = node_rects[i]; + NodeRect &nr = node_rects.write[i]; Vector2 offset = nr.node.position; int h = nr.node.size.height; @@ -771,7 +771,7 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { if (idx == -1) return; - NodeRect &nr = node_rects[idx]; + const NodeRect &nr = node_rects[idx]; Vector2 from; from.x = nr.play.position.x; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 505dd4ab76..ddd84aa563 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -197,7 +197,7 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const for (int i = 0; i < preview_images.size(); i++) { if (preview_images[i].id == p_index) { - preview_images[i].image = p_image; + preview_images.write[i].image = p_image; if (preview_images[i].button->is_pressed()) { _preview_click(p_index); } @@ -554,6 +554,8 @@ void EditorAssetLibrary::_notification(int p_what) { error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); error_label->raise(); } break; @@ -604,6 +606,8 @@ void EditorAssetLibrary::_notification(int p_what) { library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree")); error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); } break; } } diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index ddb03d0250..454a5d72f2 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -81,8 +81,8 @@ void AudioStreamEditor::_draw_preview() { float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; int idx = i; - lines[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y); - lines[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y); + lines.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y); + lines.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y); } Vector<Color> color; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 1d20c63969..145a2ef9bb 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -375,33 +375,24 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li // Handles the first element CanvasItem *canvas_item = p_list.front()->get(); - Rect2 rect; - if (canvas_item->_edit_use_rect()) { - rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); - } else { - rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2()); - } + Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); // Expand with the other ones for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); Transform2D xform = canvas_item->get_global_transform_with_canvas(); - if (canvas_item->_edit_use_rect()) { - Rect2 current_rect = canvas_item->_edit_get_rect(); - rect.expand_to(xform.xform(current_rect.position)); - rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); - rect.expand_to(xform.xform(current_rect.position + current_rect.size)); - rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y))); - } else { - rect.expand_to(xform.xform(Point2())); - } + Rect2 current_rect = canvas_item->_edit_get_rect(); + rect.expand_to(xform.xform(current_rect.position)); + rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); + rect.expand_to(xform.xform(current_rect.position + current_rect.size)); + rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y))); } return rect; } -void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { +void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, bool include_locked_nodes) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) @@ -409,12 +400,6 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); - /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != ""; - bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); - bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - - if (!lock_children && editable) {}*/ - for (int i = p_node->get_child_count() - 1; i >= 0; i--) { if (canvas_item && !canvas_item->is_set_as_toplevel()) { _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); @@ -424,28 +409,17 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c } } - if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) { + if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !canvas_item->has_meta("_edit_lock_"))) { Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); - if (canvas_item->_edit_use_rect()) { - Rect2 rect = canvas_item->_edit_get_rect(); - if (r_first) { - r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); - r_first = false; - } - if (r_rect.size != Size2()) { - r_rect.expand_to(xform.xform(rect.position)); - r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); - r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); - r_rect.expand_to(xform.xform(rect.position + rect.size)); - } - } else { - if (r_first) { - r_rect = Rect2(xform.xform(Point2()), Size2()); - r_first = false; - } else { - r_rect.expand_to(xform.xform(Point2())); - } + Rect2 rect = canvas_item->_edit_get_rect(); + if (r_first) { + r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); + r_first = false; } + r_rect.expand_to(xform.xform(rect.position)); + r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); + r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); + r_rect.expand_to(xform.xform(rect.position + rect.size)); } } @@ -533,7 +507,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel r_items.remove(i); i--; } else { - r_items[i].item = canvas_item; + r_items.write[i].item = canvas_item; } } } @@ -4879,7 +4853,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index adc4010f39..2c943385ad 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -390,7 +390,7 @@ class CanvasItemEditor : public VBoxContainer { List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); - void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); Object *_get_editor_data(Object *p_what); diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index e837359d0c..5109379add 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -276,7 +276,7 @@ bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<Inpu //apply ERR_FAIL_INDEX_V(edited_point, poly.size(), false); - poly[edited_point] = edited_point_pos; + poly.write[edited_point] = edited_point_pos; undo_redo->create_action(TTR("Edit Poly")); undo_redo->add_do_method(node, "set_polygon", poly); undo_redo->add_undo_method(node, "set_polygon", pre_move_edit); diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index b003664dca..9e052bb027 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -446,8 +446,8 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { float radius = shape->get_radius(); float height = shape->get_height() / 2; - handles[0] = Point2(radius, -height); - handles[1] = Point2(0, -(height + radius)); + handles.write[0] = Point2(radius, -height); + handles.write[1] = Point2(0, -(height + radius)); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); @@ -458,7 +458,7 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { Ref<CircleShape2D> shape = node->get_shape(); handles.resize(1); - handles[0] = Point2(shape->get_radius(), 0); + handles.write[0] = Point2(shape->get_radius(), 0); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); @@ -476,8 +476,8 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { Ref<LineShape2D> shape = node->get_shape(); handles.resize(2); - handles[0] = shape->get_normal() * shape->get_d(); - handles[1] = shape->get_normal() * (shape->get_d() + 30.0); + handles.write[0] = shape->get_normal() * shape->get_d(); + handles.write[1] = shape->get_normal() * (shape->get_d() + 30.0); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); @@ -488,7 +488,7 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { Ref<RayShape2D> shape = node->get_shape(); handles.resize(1); - handles[0] = Point2(0, shape->get_length()); + handles.write[0] = Point2(0, shape->get_length()); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); @@ -499,8 +499,8 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { handles.resize(2); Vector2 ext = shape->get_extents(); - handles[0] = Point2(ext.x, 0); - handles[1] = Point2(0, -ext.y); + handles.write[0] = Point2(ext.x, 0); + handles.write[1] = Point2(0, -ext.y); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); @@ -511,8 +511,8 @@ void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) { Ref<SegmentShape2D> shape = node->get_shape(); handles.resize(2); - handles[0] = shape->get_a(); - handles[1] = shape->get_b(); + handles.write[0] = shape->get_a(); + handles.write[1] = shape->get_b(); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 0d25b3685a..9acbceec92 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from) const { Ref<Image> img; Ref<AtlasTexture> atex = p_from; @@ -138,12 +138,66 @@ EditorTexturePreviewPlugin::EditorTexturePreviewPlugin() { //////////////////////////////////////////////////////////////////////////// +bool EditorImagePreviewPlugin::handles(const String &p_type) const { + + return p_type == "Image"; +} + +Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from) const { + + Ref<Image> img = p_from; + + if (img.is_null() || img->empty()) + return Ref<Image>(); + + img = img->duplicate(); + img->clear_mipmaps(); + + int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size"); + thumbnail_size *= EDSCALE; + if (img->is_compressed()) { + if (img->decompress() != OK) + return Ref<Image>(); + } else if (img->get_format() != Image::FORMAT_RGB8 && img->get_format() != Image::FORMAT_RGBA8) { + img->convert(Image::FORMAT_RGBA8); + } + + int width, height; + if (img->get_width() > thumbnail_size && img->get_width() >= img->get_height()) { + + width = thumbnail_size; + height = img->get_height() * thumbnail_size / img->get_width(); + } else if (img->get_height() > thumbnail_size && img->get_height() >= img->get_width()) { + + height = thumbnail_size; + width = img->get_width() * thumbnail_size / img->get_height(); + } else { + + width = img->get_width(); + height = img->get_height(); + } + + img->resize(width, height); + post_process_preview(img); + + Ref<ImageTexture> ptex; + ptex.instance(); + + ptex->create_from_image(img, 0); + return ptex; +} + +EditorImagePreviewPlugin::EditorImagePreviewPlugin() { +} + +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////// bool EditorBitmapPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "BitMap"); } -Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from) const { Ref<BitMap> bm = p_from; @@ -215,12 +269,12 @@ bool EditorPackedScenePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "PackedScene"); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) const { return generate_from_path(p_from->get_path()); } -Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) { +Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) const { String temp_path = EditorSettings::get_singleton()->get_cache_dir(); String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); @@ -269,7 +323,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) const { Ref<Material> material = p_from; ERR_FAIL_COND_V(material.is_null(), Ref<Texture>()); @@ -281,7 +335,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -436,7 +490,7 @@ bool EditorScriptPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Script"); } -Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from) const { Ref<Script> scr = p_from; if (scr.is_null()) @@ -559,7 +613,7 @@ bool EditorAudioStreamPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "AudioStream"); } -Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorAudioStreamPreviewPlugin::generate(const RES &p_from) const { Ref<AudioStream> stream = p_from; ERR_FAIL_COND_V(stream.is_null(), Ref<Texture>()); @@ -657,7 +711,7 @@ bool EditorMeshPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Mesh"); //any Mesh } -Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) const { Ref<Mesh> mesh = p_from; ERR_FAIL_COND_V(mesh.is_null(), Ref<Texture>()); @@ -684,7 +738,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -771,16 +825,7 @@ bool EditorFontPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "DynamicFontData"); } -Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { - if (canvas.is_valid()) { - VS::get_singleton()->viewport_remove_canvas(viewport, canvas); - } - - canvas = VS::get_singleton()->canvas_create(); - canvas_item = VS::get_singleton()->canvas_item_create(); - - VS::get_singleton()->viewport_attach_canvas(viewport, canvas); - VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas); +Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) const { Ref<DynamicFontData> SampledFont; SampledFont.instance(); @@ -809,7 +854,7 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture preview_done = false; - VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant()); + VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); @@ -829,7 +874,7 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path) { return ptex; } -Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) { +Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from) const { return generate_from_path(p_from->get_path()); } @@ -842,6 +887,12 @@ EditorFontPreviewPlugin::EditorFontPreviewPlugin() { VS::get_singleton()->viewport_set_size(viewport, 128, 128); VS::get_singleton()->viewport_set_active(viewport, true); viewport_texture = VS::get_singleton()->viewport_get_texture(viewport); + + canvas = VS::get_singleton()->canvas_create(); + canvas_item = VS::get_singleton()->canvas_item_create(); + + VS::get_singleton()->viewport_attach_canvas(viewport, canvas); + VS::get_singleton()->canvas_item_set_parent(canvas_item, canvas); } EditorFontPreviewPlugin::~EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 140d9f849f..8bd7943383 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,16 +39,25 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorTexturePreviewPlugin(); }; +class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { + GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) +public: + virtual bool handles(const String &p_type) const; + virtual Ref<Texture> generate(const RES &p_from) const; + + EditorImagePreviewPlugin(); +}; + class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorBitmapPreviewPlugin(); }; @@ -57,8 +66,8 @@ class EditorPackedScenePreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); - virtual Ref<Texture> generate_from_path(const String &p_path); + virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate_from_path(const String &p_path) const; EditorPackedScenePreviewPlugin(); }; @@ -77,7 +86,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -86,7 +95,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorMaterialPreviewPlugin(); ~EditorMaterialPreviewPlugin(); @@ -95,7 +104,7 @@ public: class EditorScriptPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorScriptPreviewPlugin(); }; @@ -103,7 +112,7 @@ public: class EditorAudioStreamPreviewPlugin : public EditorResourcePreviewGenerator { public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorAudioStreamPreviewPlugin(); }; @@ -121,7 +130,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -130,7 +139,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); + virtual Ref<Texture> generate(const RES &p_from) const; EditorMeshPreviewPlugin(); ~EditorMeshPreviewPlugin(); @@ -144,7 +153,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { RID viewport_texture; RID canvas; RID canvas_item; - volatile bool preview_done; + mutable volatile bool preview_done; void _preview_done(const Variant &p_udata); @@ -153,8 +162,8 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual Ref<Texture> generate(const RES &p_from); - virtual Ref<Texture> generate_from_path(const String &p_path); + virtual Ref<Texture> generate(const RES &p_from) const; + virtual Ref<Texture> generate_from_path(const String &p_path) const; EditorFontPreviewPlugin(); ~EditorFontPreviewPlugin(); diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index a3be10dc33..3351e5918f 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -255,7 +255,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { //apply ERR_FAIL_INDEX_V(edited_point, poly.size(), false); - poly[edited_point] = edited_point_pos; + poly.write[edited_point] = edited_point_pos; undo_redo->create_action(TTR("Edit Poly")); undo_redo->add_do_method(node->get_occluder_polygon().ptr(), "set_polygon", poly); undo_redo->add_undo_method(node->get_occluder_polygon().ptr(), "set_polygon", pre_move_edit); diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index 6d11079759..c2b17189ef 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -165,12 +165,12 @@ void Particles2DEditorPlugin::_generate_emission_mask() { if (emode == EMISSION_MODE_SOLID) { if (capture_colors) { - valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; - valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; - valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; - valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; } - valid_positions[vpc++] = Point2(i, j); + valid_positions.write[vpc++] = Point2(i, j); } else { @@ -189,7 +189,7 @@ void Particles2DEditorPlugin::_generate_emission_mask() { } if (on_border) { - valid_positions[vpc] = Point2(i, j); + valid_positions.write[vpc] = Point2(i, j); if (emode == EMISSION_MODE_BORDER_DIRECTED) { Vector2 normal; @@ -206,14 +206,14 @@ void Particles2DEditorPlugin::_generate_emission_mask() { } normal.normalize(); - valid_normals[vpc] = normal; + valid_normals.write[vpc] = normal; } if (capture_colors) { - valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; - valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; - valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; - valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; } vpc++; diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index e0325702a8..1f5a4a8a36 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -40,7 +40,6 @@ bool ParticlesEditorBase::_generate(PoolVector<Vector3> &points, PoolVector<Vect float area_accum = 0; Map<float, int> triangle_area_map; - print_line("geometry size: " + itos(geometry.size())); for (int i = 0; i < geometry.size(); i++) { @@ -300,6 +299,10 @@ void ParticlesEditor::_menu_option(int p_option) { CPUParticles *cpu_particles = memnew(CPUParticles); cpu_particles->convert_from_particles(node); + cpu_particles->set_name(node->get_name()); + cpu_particles->set_transform(node->get_transform()); + cpu_particles->set_visible(node->is_visible()); + cpu_particles->set_pause_mode(node->get_pause_mode()); undo_redo->create_action("Replace Particles by CPUParticles"); undo_redo->add_do_method(node, "replace_by", cpu_particles); diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 72a8b55a52..618c70d1a1 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -215,6 +215,10 @@ void PathSpatialGizmo::redraw() { clear(); + Ref<SpatialMaterial> path_material = gizmo_plugin->get_material("path_material"); + Ref<SpatialMaterial> path_thin_material = gizmo_plugin->get_material("path_thin_material"); + Ref<SpatialMaterial> handles_material = gizmo_plugin->get_material("handles"); + Ref<Curve3D> c = path->get_curve(); if (c.is_null()) return; @@ -238,7 +242,7 @@ void PathSpatialGizmo::redraw() { } if (v3p.size() > 1) { - add_lines(v3p, PathEditorPlugin::singleton->path_material); + add_lines(v3p, path_material); add_collision_segments(v3p); } @@ -265,13 +269,13 @@ void PathSpatialGizmo::redraw() { } if (v3p.size() > 1) { - add_lines(v3p, PathEditorPlugin::singleton->path_thin_material); + add_lines(v3p, path_thin_material); } if (handles.size()) { - add_handles(handles); + add_handles(handles, handles_material); } if (sec_handles.size()) { - add_handles(sec_handles, false, true); + add_handles(sec_handles, handles_material, false, true); } } } @@ -282,16 +286,6 @@ PathSpatialGizmo::PathSpatialGizmo(Path *p_path) { set_spatial_node(p_path); } -Ref<SpatialEditorGizmo> PathEditorPlugin::create_spatial_gizmo(Spatial *p_spatial) { - - if (Object::cast_to<Path>(p_spatial)) { - - return memnew(PathSpatialGizmo(Object::cast_to<Path>(p_spatial))); - } - - return Ref<SpatialEditorGizmo>(); -} - bool PathEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { if (!path) @@ -567,21 +561,9 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) { mirror_handle_angle = true; mirror_handle_length = true; - path_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - path_material->set_albedo(Color(0.5, 0.5, 1.0, 0.8)); - path_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - path_material->set_line_width(3); - path_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); - path_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - - path_thin_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - path_thin_material->set_albedo(Color(0.5, 0.5, 1.0, 0.4)); - path_thin_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - path_thin_material->set_line_width(1); - path_thin_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); - path_thin_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - - //SpatialEditor::get_singleton()->add_gizmo_plugin(this); + Ref<PathSpatialGizmoPlugin> gizmo_plugin; + gizmo_plugin.instance(); + SpatialEditor::get_singleton()->register_gizmo_plugin(gizmo_plugin); sep = memnew(VSeparator); sep->hide(); @@ -630,18 +612,53 @@ PathEditorPlugin::PathEditorPlugin(EditorNode *p_node) { curve_edit->set_pressed(true); /* - collision_polygon_editor = memnew( PathEditor(p_node) ); - editor->get_viewport()->add_child(collision_polygon_editor); + collision_polygon_editor = memnew( PathEditor(p_node) ); + editor->get_viewport()->add_child(collision_polygon_editor); + collision_polygon_editor->set_margin(MARGIN_LEFT,200); + collision_polygon_editor->set_margin(MARGIN_RIGHT,230); + collision_polygon_editor->set_margin(MARGIN_TOP,0); + collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); + collision_polygon_editor->hide(); + */ +} - collision_polygon_editor->set_margin(MARGIN_LEFT,200); - collision_polygon_editor->set_margin(MARGIN_RIGHT,230); - collision_polygon_editor->set_margin(MARGIN_TOP,0); - collision_polygon_editor->set_margin(MARGIN_BOTTOM,10); +PathEditorPlugin::~PathEditorPlugin() { +} + +Ref<EditorSpatialGizmo> PathSpatialGizmoPlugin::create_gizmo(Spatial *p_spatial) { + Ref<PathSpatialGizmo> ref; + Path *path = Object::cast_to<Path>(p_spatial); + if (path) ref = Ref<PathSpatialGizmo>(memnew(PathSpatialGizmo(path))); - collision_polygon_editor->hide(); - */ + return ref; } -PathEditorPlugin::~PathEditorPlugin() { +String PathSpatialGizmoPlugin::get_name() const { + return "Path"; +} + +PathSpatialGizmoPlugin::PathSpatialGizmoPlugin() { + + Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8)); + + Ref<SpatialMaterial> path_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + path_color.a = 0.8; + path_material->set_albedo(path_color); + path_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + path_material->set_line_width(3); + path_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + path_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + + Ref<SpatialMaterial> path_thin_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + path_color.a = 0.4; + path_thin_material->set_albedo(path_color); + path_thin_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + path_thin_material->set_line_width(1); + path_thin_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + path_thin_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + + add_material("path_material", path_material); + add_material("path_thin_material", path_thin_material); + create_handle_material("handles"); } diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h index 52dfb78b61..61f309e794 100644 --- a/editor/plugins/path_editor_plugin.h +++ b/editor/plugins/path_editor_plugin.h @@ -49,10 +49,22 @@ public: virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(); + virtual void redraw(); PathSpatialGizmo(Path *p_path = NULL); }; +class PathSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(PathSpatialGizmoPlugin, EditorSpatialGizmoPlugin); + +protected: + Ref<EditorSpatialGizmo> create_gizmo(Spatial *p_spatial); + +public: + String get_name() const; + PathSpatialGizmoPlugin(); +}; + class PathEditorPlugin : public EditorPlugin { GDCLASS(PathEditorPlugin, EditorPlugin); @@ -88,12 +100,10 @@ public: Path *get_edited_path() { return path; } static PathEditorPlugin *singleton; - Ref<SpatialMaterial> path_material; - Ref<SpatialMaterial> path_thin_material; virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); //virtual bool forward_gui_input(const InputEvent& p_event) { return collision_polygon_editor->forward_gui_input(p_event); } - virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); + //virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); virtual String get_name() const { return "Path"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 876da7f61a..7d72537e32 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -31,7 +31,6 @@ #include "script_editor_plugin.h" #include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" #include "core/os/file_access.h" #include "core/os/input.h" #include "core/os/keyboard.h" @@ -220,6 +219,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", this, "_confirmed"); + + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } break; } } @@ -283,7 +285,6 @@ void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) { - continue; } @@ -344,11 +345,11 @@ void ScriptEditor::_save_history() { if (Object::cast_to<ScriptEditorBase>(n)) { - history[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); + history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); } if (Object::cast_to<EditorHelp>(n)) { - history[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); + history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); } } @@ -375,11 +376,11 @@ void ScriptEditor::_go_to_tab(int p_idx) { if (Object::cast_to<ScriptEditorBase>(n)) { - history[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); + history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); } if (Object::cast_to<EditorHelp>(n)) { - history[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); + history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); } } @@ -402,7 +403,10 @@ void ScriptEditor::_go_to_tab(int p_idx) { if (is_visible_in_tree()) Object::cast_to<ScriptEditorBase>(c)->ensure_focus(); - notify_script_changed(Object::cast_to<ScriptEditorBase>(c)->get_edited_script()); + Ref<Script> script = Object::cast_to<ScriptEditorBase>(c)->get_edited_resource(); + if (script != NULL) { + notify_script_changed(script); + } } if (Object::cast_to<EditorHelp>(c)) { @@ -482,12 +486,23 @@ void ScriptEditor::_open_recent_script(int p_idx) { String path = rc[p_idx]; // if its not on disk its a help file or deleted if (FileAccess::exists(path)) { - Ref<Script> script = ResourceLoader::load(path); - if (script.is_valid()) { - edit(script, true); - return; + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + + if (extensions.find(path.get_extension())) { + Ref<Script> script = ResourceLoader::load(path); + if (script.is_valid()) { + edit(script, true); + return; + } } + Error err; + Ref<TextFile> text_file = _load_text_file(path, &err); + if (text_file.is_valid()) { + edit(text_file, true); + return; + } // if it's a path then its most likely a deleted file not help } else if (!path.is_resource_file()) { _help_class_open(path); @@ -513,12 +528,17 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { return; Node *tselected = tab_container->get_child(selected); + ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { if (p_save) { apply_scripts(); } - notify_script_close(current->get_edited_script()); + + Ref<Script> script = current->get_edited_resource(); + if (script != NULL) { + notify_script_close(script); + } } // roll back to previous tab @@ -589,7 +609,7 @@ void ScriptEditor::_close_docs_tab() { void ScriptEditor::_copy_script_path() { ScriptEditorBase *se = _get_current_editor(); - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); OS::get_singleton()->set_clipboard(script->get_path()); } @@ -655,7 +675,7 @@ void ScriptEditor::_resave_scripts(const String &p_str) { if (!se) continue; - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) continue; //internal script, who cares @@ -672,7 +692,14 @@ void ScriptEditor::_resave_scripts(const String &p_str) { } } - editor->save_resource(script); + Ref<TextFile> text_file = script; + if (text_file != NULL) { + se->apply_code(); + _save_text_file(text_file, text_file->get_path()); + break; + } else { + editor->save_resource(script); + } se->tag_saved_version(); } @@ -689,25 +716,37 @@ void ScriptEditor::_reload_scripts() { continue; } - Ref<Script> script = se->get_edited_script(); + RES edited_res = se->get_edited_resource(); - if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) { + if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) { continue; //internal script, who cares } - uint64_t last_date = script->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(script->get_path()); + uint64_t last_date = edited_res->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); if (last_date == date) { continue; } - Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true); - ERR_CONTINUE(!rel_script.is_valid()); - script->set_source_code(rel_script->get_source_code()); - script->set_last_modified_time(rel_script->get_last_modified_time()); - script->reload(); + Ref<Script> script = edited_res; + if (script != NULL) { + Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true); + ERR_CONTINUE(!rel_script.is_valid()); + script->set_source_code(rel_script->get_source_code()); + script->set_last_modified_time(rel_script->get_last_modified_time()); + script->reload(); + } + + Ref<TextFile> text_file = edited_res; + if (text_file != NULL) { + Error err; + Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err); + ERR_CONTINUE(!rel_text_file.is_valid()); + text_file->set_text(rel_text_file->get_text()); + text_file->set_last_modified_time(rel_text_file->get_last_modified_time()); + } se->reload_text(); } @@ -725,7 +764,7 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) { continue; } - Ref<Script> script = se->get_edited_script(); + RES script = se->get_edited_resource(); if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) { continue; //internal script, who cares @@ -750,7 +789,7 @@ void ScriptEditor::_live_auto_reload_running_scripts() { debugger->reload_scripts(); } -bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { +bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { disk_changed_list->clear(); TreeItem *r = disk_changed_list->create_item(); @@ -765,21 +804,20 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (se) { - Ref<Script> script = se->get_edited_script(); - - if (p_for_script.is_valid() && p_for_script != script) + RES edited_res = se->get_edited_resource(); + if (edited_res.is_valid() && p_for_script != edited_res) continue; - if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) + if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) continue; //internal script, who cares - uint64_t last_date = script->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(script->get_path()); + uint64_t last_date = edited_res->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); if (last_date != date) { TreeItem *ti = disk_changed_list->create_item(r); - ti->set_text(0, script->get_path().get_file()); + ti->set_text(0, edited_res->get_path().get_file()); if (!use_autoreload || se->is_unsaved()) { need_ask = true; @@ -804,6 +842,49 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Script> p_for_script) { void ScriptEditor::_file_dialog_action(String p_file) { switch (file_dialog_option) { + case FILE_OPEN: { + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + if (extensions.find(p_file.get_extension())) { + Ref<Script> scr = ResourceLoader::load(p_file); + if (!scr.is_valid()) { + editor->show_warning(TTR("Error could not load file."), TTR("Error!")); + file_dialog_option = -1; + return; + } + + edit(scr); + file_dialog_option = -1; + return; + } + + Error error; + Ref<TextFile> text_file = _load_text_file(p_file, &error); + if (error != OK) { + editor->show_warning(TTR("Error could not load file."), TTR("Error!")); + } + + if (text_file.is_valid()) { + edit(text_file); + file_dialog_option = -1; + return; + } + } + case FILE_SAVE_AS: { + ScriptEditorBase *current = _get_current_editor(); + + String path = ProjectSettings::get_singleton()->localize_path(p_file); + Error err = _save_text_file(current->get_edited_resource(), path); + + if (err != OK) { + editor->show_accept(TTR("Error saving file!"), TTR("OK")); + return; + } + + ((Resource *)current->get_edited_resource().ptr())->set_path(path); + _update_script_names(); + } break; case THEME_SAVE_AS: { if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) { editor->show_warning(TTR("Error while saving theme"), TTR("Error saving")); @@ -823,7 +904,8 @@ Ref<Script> ScriptEditor::_get_current_script() { ScriptEditorBase *current = _get_current_editor(); if (current) { - return current->get_edited_script(); + Ref<Script> script = current->get_edited_resource(); + return script != NULL ? script : NULL; } else { return NULL; } @@ -848,8 +930,19 @@ void ScriptEditor::_menu_option(int p_option) { script_create_dialog->popup_centered(Size2(300, 300) * EDSCALE); } break; case FILE_OPEN: { + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = FILE_OPEN; - editor->open_resource("Script"); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + file_dialog->clear_filters(); + for (int i = 0; i < extensions.size(); i++) { + file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper()); + } + + file_dialog->popup_centered_ratio(); + file_dialog->set_title(TTR("Open File")); return; } break; case FILE_SAVE_ALL: { @@ -929,7 +1022,14 @@ void ScriptEditor::_menu_option(int p_option) { current->convert_indent_to_tabs(); } } - editor->save_resource(current->get_edited_script()); + + Ref<TextFile> text_file = current->get_edited_resource(); + if (text_file != NULL) { + current->apply_code(); + _save_text_file(text_file, text_file->get_path()); + break; + } + editor->save_resource(current->get_edited_resource()); } break; case FILE_SAVE_AS: { @@ -943,8 +1043,25 @@ void ScriptEditor::_menu_option(int p_option) { current->convert_indent_to_tabs(); } } - editor->push_item(Object::cast_to<Object>(current->get_edited_script().ptr())); - editor->save_resource_as(current->get_edited_script()); + + Ref<TextFile> text_file = current->get_edited_resource(); + if (text_file != NULL) { + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = FILE_SAVE_AS; + + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + file_dialog->clear_filters(); + file_dialog->set_current_dir(text_file->get_path().get_base_dir()); + file_dialog->set_current_file(text_file->get_path().get_file()); + file_dialog->popup_centered_ratio(); + file_dialog->set_title(TTR("Save File As...")); + break; + } + + editor->push_item(Object::cast_to<Object>(current->get_edited_resource().ptr())); + editor->save_resource_as(current->get_edited_resource()); } break; @@ -956,8 +1073,8 @@ void ScriptEditor::_menu_option(int p_option) { } break; case FILE_RUN: { - Ref<Script> scr = current->get_edited_script(); - if (scr.is_null()) { + Ref<Script> scr = current->get_edited_resource(); + if (scr == NULL || scr.is_null()) { EditorNode::get_singleton()->show_warning("Can't obtain the script for running"); break; } @@ -1000,8 +1117,7 @@ void ScriptEditor::_menu_option(int p_option) { _copy_script_path(); } break; case SHOW_IN_FILE_SYSTEM: { - ScriptEditorBase *se = _get_current_editor(); - Ref<Script> script = se->get_edited_script(); + RES script = current->get_edited_resource(); FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock(); file_system_dock->navigate_to_path(script->get_path()); // Ensure that the FileSystem dock is visible. @@ -1259,8 +1375,8 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) { if (se) { - Ref<Script> script = se->get_edited_script(); - if (!script.is_valid()) + Ref<Script> script = se->get_edited_resource(); + if (script == NULL || !script.is_valid()) continue; if (script->get_path().find("::") != -1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed @@ -1307,9 +1423,13 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { if (!se) continue; + Ref<Script> script = se->get_edited_resource(); + if (script == NULL) { + continue; + } + List<int> bpoints; se->get_breakpoints(&bpoints); - Ref<Script> script = se->get_edited_script(); String base = script->get_path(); ERR_CONTINUE(base.begins_with("local://") || base == ""); @@ -1452,7 +1572,7 @@ void ScriptEditor::_update_members_overview() { members_overview->set_item_metadata(i, functions[i].get_slice(":", 1).to_int() - 1); } - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); bool built_in = !path.is_resource_file(); String name = built_in ? path.get_file() : se->get_name(); filename->set_text(name); @@ -1570,7 +1690,7 @@ void ScriptEditor::_update_script_names() { if (se) { Ref<Texture> icon = se->get_icon(); - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); bool built_in = !path.is_resource_file(); String name = built_in ? path.get_file() : se->get_name(); @@ -1579,7 +1699,7 @@ void ScriptEditor::_update_script_names() { sd.name = name; sd.tooltip = path; sd.index = i; - sd.used = used.has(se->get_edited_script()); + sd.used = used.has(se->get_edited_resource()); sd.category = 0; sd.ref = se; @@ -1681,11 +1801,65 @@ void ScriptEditor::_update_script_names() { _update_script_colors(); } -bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool p_grab_focus) { +Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } - if (p_script.is_null()) + String local_path = ProjectSettings::get_singleton()->localize_path(p_path); + String path = ResourceLoader::path_remap(local_path); + + TextFile *text_file = memnew(TextFile); + Ref<TextFile> text_res(text_file); + Error err = text_file->load_text(path); + + if (err != OK) { + ERR_FAIL_COND_V(err != OK, RES()); + } + + text_file->set_file_path(local_path); + text_file->set_path(local_path, true); + + if (r_error) { + *r_error = OK; + } + + return text_res; +} + +Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_path) { + Ref<TextFile> sqscr = p_text_file; + ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); + + String source = sqscr->get_text(); + + Error err; + FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + + if (err) { + + ERR_FAIL_COND_V(err, err); + } + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + memdelete(file); + return ERR_CANT_CREATE; + } + file->close(); + memdelete(file); + + _res_saved_callback(sqscr); + return OK; +} + +bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus) { + + if (p_resource.is_null()) return false; + Ref<Script> script = p_resource; + // refuse to open built-in if scene is not loaded // see if already has it @@ -1694,17 +1868,18 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool const bool should_open = open_dominant || !EditorNode::get_singleton()->is_changing_scene(); - if (p_script->get_language()->overrides_external_editor()) { + if (script != NULL && script->get_language()->overrides_external_editor()) { if (should_open) { - Error err = p_script->get_language()->open_in_external_editor(p_script, p_line >= 0 ? p_line : 0, p_col); + Error err = script->get_language()->open_in_external_editor(script, p_line >= 0 ? p_line : 0, p_col); if (err != OK) ERR_PRINT("Couldn't open script in the overridden external text editor"); } return false; } - if ((debugger->get_dump_stack_script() != p_script || debugger->get_debug_with_external_editor()) && - p_script->get_path().is_resource_file() && + if ((debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) && + p_resource->get_path().is_resource_file() && + p_resource->get_class_name() != StringName("VisualScript") && bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) { String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path"); @@ -1714,7 +1889,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool if (flags.size()) { String project_path = ProjectSettings::get_singleton()->get_resource_path(); - String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path()); + String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path()); flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0)); flags = flags.replacen("{col}", itos(p_col)); @@ -1762,7 +1937,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool if (!se) continue; - if (se->get_edited_script() == p_script) { + if (se->get_edited_resource() == p_resource) { if (should_open) { if (tab_container->get_current_tab() != i) { @@ -1784,7 +1959,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool ScriptEditorBase *se; for (int i = script_editor_func_count - 1; i >= 0; i--) { - se = script_editor_funcs[i](p_script); + se = script_editor_funcs[i](p_resource); if (se) break; } @@ -1795,9 +1970,9 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i](); se->add_syntax_highlighter(highlighter); - if (!highlighter_set) { + if (script != NULL && !highlighter_set) { List<String> languages = highlighter->get_supported_languages(); - if (languages.find(p_script->get_language()->get_name())) { + if (languages.find(script->get_language()->get_name())) { se->set_syntax_highlighter(highlighter); highlighter_set = true; } @@ -1805,7 +1980,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool } tab_container->add_child(se); - se->set_edited_script(p_script); + se->set_edited_resource(p_resource); se->set_tooltip_request_func("_get_debug_tooltip", this); if (se->get_edit_menu()) { se->get_edit_menu()->hide(); @@ -1829,14 +2004,14 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool //test for modification, maybe the script was not edited but was loaded - _test_script_times_on_disk(p_script); - _update_modified_scripts_for_external_editor(p_script); + _test_script_times_on_disk(p_resource); + _update_modified_scripts_for_external_editor(p_resource); if (p_line >= 0) se->goto_line(p_line - 1); - notify_script_changed(p_script); - _add_recent_script(p_script->get_path()); + notify_script_changed(p_resource); + _add_recent_script(p_resource->get_path()); return true; } @@ -1863,12 +2038,19 @@ void ScriptEditor::save_all_scripts() { if (!se->is_unsaved()) continue; - Ref<Script> script = se->get_edited_script(); - if (script.is_valid()) + RES edited_res = se->get_edited_resource(); + if (edited_res.is_valid()) { se->apply_code(); + } - if (script->get_path() != "" && script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) - editor->save_resource(script); //external script, save it + if (edited_res->get_path() != "" && edited_res->get_path().find("local://") == -1 && edited_res->get_path().find("::") == -1) { + Ref<TextFile> text_file = edited_res; + if (text_file != NULL) { + _save_text_file(text_file, text_file->get_path()); + continue; + } + editor->save_resource(edited_res); //external script, save it + } } _update_script_names(); @@ -1938,7 +2120,7 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) continue; - if (se->get_edited_script() != script) + if (se->get_edited_resource() != script) continue; se->add_callback(p_function, p_args); @@ -2223,25 +2405,25 @@ void ScriptEditor::_make_script_list_context_menu() { if (se) { context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS); - context_menu->add_separator(); + } + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS); + context_menu->add_separator(); + if (se) { + Ref<Script> scr = se->get_edited_resource(); + if (scr != NULL) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); + if (!scr.is_null() && scr->is_tool()) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); + context_menu->add_separator(); + } + } context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM); - Ref<Script> scr = se->get_edited_script(); - if (!scr.is_null() && scr->is_tool()) { - context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); - } - } else { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + context_menu->add_separator(); } - EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(selected)); - - context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN); context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT); @@ -2268,14 +2450,28 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { restoring_layout = true; + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Script", &extensions); + for (int i = 0; i < scripts.size(); i++) { String path = scripts[i]; if (!FileAccess::exists(path)) continue; - Ref<Script> scr = ResourceLoader::load(path); - if (scr.is_valid()) { - edit(scr); + + if (extensions.find(path.get_extension())) { + Ref<Script> scr = ResourceLoader::load(path); + if (scr.is_valid()) { + edit(scr); + continue; + } + } + + Error error; + Ref<TextFile> text_file = _load_text_file(path, &error); + if (error == OK && text_file.is_valid()) { + edit(text_file); + continue; } } @@ -2311,7 +2507,7 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (se) { - String path = se->get_edited_script()->get_path(); + String path = se->get_edited_resource()->get_path(); if (!path.is_resource_file()) continue; @@ -2419,11 +2615,11 @@ void ScriptEditor::_update_history_pos(int p_new_pos) { if (Object::cast_to<ScriptEditorBase>(n)) { - history[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); + history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state(); } if (Object::cast_to<EditorHelp>(n)) { - history[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); + history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll(); } history_pos = p_new_pos; @@ -2436,7 +2632,10 @@ void ScriptEditor::_update_history_pos(int p_new_pos) { Object::cast_to<ScriptEditorBase>(n)->set_edit_state(history[history_pos].state); Object::cast_to<ScriptEditorBase>(n)->ensure_focus(); - notify_script_changed(Object::cast_to<ScriptEditorBase>(n)->get_edited_script()); + Ref<Script> script = Object::cast_to<ScriptEditorBase>(n)->get_edited_resource(); + if (script != NULL) { + notify_script_changed(script); + } } if (Object::cast_to<EditorHelp>(n)) { @@ -2473,7 +2672,11 @@ Vector<Ref<Script> > ScriptEditor::get_open_scripts() const { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); if (!se) continue; - out_scripts.push_back(se->get_edited_script()); + + Ref<Script> script = se->get_edited_resource(); + if (script != NULL) { + out_scripts.push_back(script); + } } return out_scripts; @@ -2519,6 +2722,14 @@ void ScriptEditor::_open_script_request(const String &p_path) { Ref<Script> script = ResourceLoader::load(p_path); if (script.is_valid()) { script_editor->edit(script, false); + return; + } + + Error err; + Ref<TextFile> text_file = script_editor->_load_text_file(p_path, &err); + if (text_file.is_valid()) { + script_editor->edit(text_file, false); + return; } } @@ -2552,7 +2763,7 @@ void ScriptEditor::_on_find_in_files_requested(String text) { void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { - Ref<Resource> res = ResourceLoader::load(fpath); + RES res = ResourceLoader::load(fpath); edit(res); ScriptEditorBase *seb = _get_current_editor(); @@ -2686,13 +2897,14 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_list->set_v_size_flags(SIZE_EXPAND_FILL); script_split->set_split_offset(140); _sort_list_on_update = true; - script_list->connect("gui_input", this, "_script_list_gui_input"); + script_list->connect("gui_input", this, "_script_list_gui_input", varray(), CONNECT_DEFERRED); script_list->set_allow_rmb_select(true); script_list->set_drag_forwarding(this); context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_menu_option"); + context_menu->set_hide_on_window_lose_focus(true); overview_vbox = memnew(VBoxContainer); overview_vbox->set_custom_minimum_size(Size2(0, 90)); @@ -2747,6 +2959,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu = memnew(MenuButton); menu_hb->add_child(file_menu); file_menu->set_text(TTR("File")); + file_menu->get_popup()->set_hide_on_window_lose_focus(true); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New")), FILE_NEW); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN); file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT); @@ -2796,6 +3009,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu = memnew(MenuButton); menu_hb->add_child(script_search_menu); script_search_menu->set_text(TTR("Search")); + script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT); script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); @@ -2804,6 +3018,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); debug_menu->set_text(TTR("Debug")); + debug_menu->get_popup()->set_hide_on_window_lose_focus(true); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT); debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP); debug_menu->get_popup()->add_separator(); @@ -2885,7 +3100,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { error_dialog = memnew(AcceptDialog); add_child(error_dialog); - error_dialog->get_ok()->set_text(TTR("I see...")); + error_dialog->get_ok()->set_text(TTR("OK")); debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); @@ -2968,14 +3183,21 @@ ScriptEditor::~ScriptEditor() { void ScriptEditorPlugin::edit(Object *p_object) { - if (!Object::cast_to<Script>(p_object)) - return; + if (Object::cast_to<Script>(p_object)) { + script_editor->edit(Object::cast_to<Script>(p_object)); + } - script_editor->edit(Object::cast_to<Script>(p_object)); + if (Object::cast_to<TextFile>(p_object)) { + script_editor->edit(Object::cast_to<TextFile>(p_object)); + } } bool ScriptEditorPlugin::handles(Object *p_object) const { + if (Object::cast_to<TextFile>(p_object)) { + return true; + } + if (Object::cast_to<Script>(p_object)) { bool valid = _can_open_in_editor(Object::cast_to<Script>(p_object)); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index ad12add53f..186c80a5f9 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -43,6 +43,7 @@ #include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" +#include "scene/resources/text_file.h" #include "script_language.h" class ScriptEditorQuickOpen : public ConfirmationDialog { @@ -74,7 +75,7 @@ class ScriptEditorDebugger; class ScriptEditorBase : public VBoxContainer { - GDCLASS(ScriptEditorBase, VBoxContainer); + GDCLASS(ScriptEditorBase, VBoxContainer) protected: static void _bind_methods(); @@ -84,9 +85,9 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0; virtual void apply_code() = 0; - virtual Ref<Script> get_edited_script() const = 0; + virtual RES get_edited_resource() const = 0; virtual Vector<String> get_functions() = 0; - virtual void set_edited_script(const Ref<Script> &p_script) = 0; + virtual void set_edited_resource(const RES &p_res) = 0; virtual void reload_text() = 0; virtual String get_name() = 0; virtual Ref<Texture> get_icon() = 0; @@ -99,7 +100,7 @@ public: virtual void convert_indent_to_tabs() = 0; virtual void ensure_focus() = 0; virtual void tag_saved_version() = 0; - virtual void reload(bool p_soft) = 0; + virtual void reload(bool p_soft) {} virtual void get_breakpoints(List<int> *p_breakpoints) = 0; virtual void add_callback(const String &p_function, PoolStringArray p_args) = 0; virtual void update_settings() = 0; @@ -116,7 +117,7 @@ public: }; typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)(); -typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script); +typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const RES &p_resource); class EditorScriptCodeCompletionCache; class FindInFilesDialog; @@ -268,7 +269,7 @@ class ScriptEditor : public PanelContainer { void _resave_scripts(const String &p_str); void _reload_scripts(); - bool _test_script_times_on_disk(Ref<Script> p_for_script = Ref<Script>()); + bool _test_script_times_on_disk(RES p_for_script = Ref<Resource>()); void _add_recent_script(String p_path); void _update_recent_scripts(); @@ -378,6 +379,9 @@ class ScriptEditor : public PanelContainer { Ref<Script> _get_current_script(); Array _get_open_scripts() const; + Ref<TextFile> _load_text_file(const String &p_path, Error *r_error); + Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path); + void _on_find_in_files_requested(String text); void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end); void _start_find_in_files(bool with_replace); @@ -400,8 +404,8 @@ public: void ensure_select_current(); - _FORCE_INLINE_ bool edit(const Ref<Script> &p_script, bool p_grab_focus = true) { return edit(p_script, -1, 0, p_grab_focus); } - bool edit(const Ref<Script> &p_script, int p_line, int p_col, bool p_grab_focus = true); + _FORCE_INLINE_ bool edit(const RES &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); } + bool edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus = true); void get_breakpoints(List<String> *p_breakpoints); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2263d782d9..522ce52234 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -66,11 +66,24 @@ void ScriptTextEditor::apply_code() { _update_member_keywords(); } -Ref<Script> ScriptTextEditor::get_edited_script() const { - +RES ScriptTextEditor::get_edited_resource() const { return script; } +void ScriptTextEditor::set_edited_resource(const RES &p_res) { + ERR_FAIL_COND(!script.is_null()); + + script = p_res; + _set_theme_for_script(); + + code_editor->get_text_edit()->set_text(script->get_source_code()); + code_editor->get_text_edit()->clear_undo_history(); + code_editor->get_text_edit()->tag_saved_version(); + + emit_signal("name_changed"); + code_editor->update_line_and_column(); +} + void ScriptTextEditor::_update_member_keywords() { member_keywords.clear(); code_editor->get_text_edit()->clear_member_keywords(); @@ -190,6 +203,7 @@ void ScriptTextEditor::_set_theme_for_script() { List<String> keywords; script->get_language()->get_reserved_words(&keywords); + for (List<String>::Element *E = keywords.front(); E; E = E->next()) { text_edit->add_keyword_color(E->get(), colors_cache.keyword_color); @@ -251,7 +265,6 @@ void ScriptTextEditor::_set_theme_for_script() { //colorize strings List<String> strings; script->get_language()->get_string_delimiters(&strings); - for (List<String>::Element *E = strings.front(); E; E = E->next()) { String string = E->get(); @@ -261,6 +274,23 @@ void ScriptTextEditor::_set_theme_for_script() { } } +void ScriptTextEditor::_toggle_warning_pannel(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + warnings_panel->set_visible(!warnings_panel->is_visible()); + } +} + +void ScriptTextEditor::_warning_clicked(Variant p_line) { + if (p_line.get_type() == Variant::INT) { + code_editor->get_text_edit()->cursor_set_line(p_line.operator int64_t()); + } else if (p_line.get_type() == Variant::DICTIONARY) { + Dictionary meta = p_line.operator Dictionary(); + code_editor->get_text_edit()->insert_at("#warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); + _validate_script(); + } +} + void ScriptTextEditor::reload_text() { ERR_FAIL_COND(script.is_null()); @@ -326,197 +356,32 @@ bool ScriptTextEditor::is_unsaved() { Variant ScriptTextEditor::get_edit_state() { - Dictionary state; + return code_editor->get_edit_state(); +} - state["scroll_position"] = code_editor->get_text_edit()->get_v_scroll(); - state["column"] = code_editor->get_text_edit()->cursor_get_column(); - state["row"] = code_editor->get_text_edit()->cursor_get_line(); +void ScriptTextEditor::set_edit_state(const Variant &p_state) { - return state; + code_editor->set_edit_state(p_state); } -void ScriptTextEditor::_convert_case(CaseStyle p_case) { - TextEdit *te = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) { - return; - } - - if (te->is_selection_active()) { - te->begin_complex_operation(); - - int begin = te->get_selection_from_line(); - int end = te->get_selection_to_line(); - int begin_col = te->get_selection_from_column(); - int end_col = te->get_selection_to_column(); - - for (int i = begin; i <= end; i++) { - int len = te->get_line(i).length(); - if (i == end) - len -= len - end_col; - if (i == begin) - len -= begin_col; - String new_line = te->get_line(i).substr(i == begin ? begin_col : 0, len); - - switch (p_case) { - case UPPER: { - new_line = new_line.to_upper(); - } break; - case LOWER: { - new_line = new_line.to_lower(); - } break; - case CAPITALIZE: { - new_line = new_line.capitalize(); - } break; - } +void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { - if (i == begin) { - new_line = te->get_line(i).left(begin_col) + new_line; - } - if (i == end) { - new_line = new_line + te->get_line(i).right(end_col); - } - te->set_line(i, new_line); - } - te->end_complex_operation(); - } + code_editor->convert_case(p_case); } void ScriptTextEditor::trim_trailing_whitespace() { - TextEdit *tx = code_editor->get_text_edit(); - - bool trimed_whitespace = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - if (line.ends_with(" ") || line.ends_with("\t")) { - - if (!trimed_whitespace) { - tx->begin_complex_operation(); - trimed_whitespace = true; - } - - int end = 0; - for (int j = line.length() - 1; j > -1; j--) { - if (line[j] != ' ' && line[j] != '\t') { - end = j + 1; - break; - } - } - tx->set_line(i, line.substr(0, end)); - } - } - if (trimed_whitespace) { - tx->end_complex_operation(); - tx->update(); - } + code_editor->trim_trailing_whitespace(); } void ScriptTextEditor::convert_indent_to_spaces() { - TextEdit *tx = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - - if (scr.is_null()) { - return; - } - - int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); - String indent = ""; - - for (int i = 0; i < indent_size; i++) { - indent += " "; - } - - int cursor_line = tx->cursor_get_line(); - int cursor_column = tx->cursor_get_column(); - - bool changed_indentation = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - if (line.length() <= 0) { - continue; - } - - int j = 0; - while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { - if (line[j] == '\t') { - if (!changed_indentation) { - tx->begin_complex_operation(); - changed_indentation = true; - } - if (cursor_line == i && cursor_column > j) { - cursor_column += indent_size - 1; - } - line = line.left(j) + indent + line.right(j + 1); - } - j++; - } - if (changed_indentation) { - tx->set_line(i, line); - } - } - if (changed_indentation) { - tx->cursor_set_column(cursor_column); - tx->end_complex_operation(); - tx->update(); - } + code_editor->convert_indent_to_spaces(); } void ScriptTextEditor::convert_indent_to_tabs() { - TextEdit *tx = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); - - if (scr.is_null()) { - return; - } - - int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size"); - indent_size -= 1; - - int cursor_line = tx->cursor_get_line(); - int cursor_column = tx->cursor_get_column(); - - bool changed_indentation = false; - for (int i = 0; i < tx->get_line_count(); i++) { - String line = tx->get_line(i); - - if (line.length() <= 0) { - continue; - } - int j = 0; - int space_count = -1; - while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { - if (line[j] != '\t') { - space_count++; - - if (space_count == indent_size) { - if (!changed_indentation) { - tx->begin_complex_operation(); - changed_indentation = true; - } - if (cursor_line == i && cursor_column > j) { - cursor_column -= indent_size; - } - line = line.left(j - indent_size) + "\t" + line.right(j + 1); - j = 0; - space_count = -1; - } - } else { - space_count = -1; - } - j++; - } - if (changed_indentation) { - tx->set_line(i, line); - } - } - if (changed_indentation) { - tx->cursor_set_column(cursor_column); - tx->end_complex_operation(); - tx->update(); - } + code_editor->convert_indent_to_tabs(); } void ScriptTextEditor::tag_saved_version() { @@ -525,31 +390,17 @@ void ScriptTextEditor::tag_saved_version() { } void ScriptTextEditor::goto_line(int p_line, bool p_with_error) { - TextEdit *tx = code_editor->get_text_edit(); - tx->deselect(); - tx->unfold_line(p_line); - tx->call_deferred("cursor_set_line", p_line); -} -void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { - TextEdit *tx = code_editor->get_text_edit(); - tx->unfold_line(p_line); - tx->call_deferred("cursor_set_line", p_line); - tx->call_deferred("cursor_set_column", p_begin); - tx->select(p_line, p_begin, p_line, p_end); + code_editor->goto_line(p_line); } -void ScriptTextEditor::ensure_focus() { +void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { - code_editor->get_text_edit()->grab_focus(); + code_editor->goto_line_selection(p_line, p_begin, p_end); } -void ScriptTextEditor::set_edit_state(const Variant &p_state) { +void ScriptTextEditor::ensure_focus() { - Dictionary state = p_state; - code_editor->get_text_edit()->cursor_set_column(state["column"]); - code_editor->get_text_edit()->cursor_set_line(state["row"]); - code_editor->get_text_edit()->set_v_scroll(state["scroll_position"]); code_editor->get_text_edit()->grab_focus(); } @@ -578,22 +429,6 @@ Ref<Texture> ScriptTextEditor::get_icon() { return Ref<Texture>(); } -void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) { - - ERR_FAIL_COND(!script.is_null()); - - script = p_script; - _set_theme_for_script(); - - code_editor->get_text_edit()->set_text(script->get_source_code()); - code_editor->get_text_edit()->clear_undo_history(); - code_editor->get_text_edit()->tag_saved_version(); - - emit_signal("name_changed"); - code_editor->update_line_and_column(); - call_deferred("_validate_script"); -} - void ScriptTextEditor::_validate_script() { String errortxt; @@ -603,8 +438,9 @@ void ScriptTextEditor::_validate_script() { String text = te->get_text(); List<String> fnc; Set<int> safe_lines; + List<ScriptLanguage::Warning> warnings; - if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc, &safe_lines)) { + if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc, &warnings, &safe_lines)) { String error_text = "error(" + itos(line) + "," + itos(col) + "): " + errortxt; code_editor->set_error(error_text); } else { @@ -624,6 +460,37 @@ void ScriptTextEditor::_validate_script() { } } + code_editor->get_warning_count_label()->set_text(itos(warnings.size())); + warnings_panel->clear(); + warnings_panel->push_table(3); + for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) { + ScriptLanguage::Warning w = E->get(); + + warnings_panel->push_cell(); + warnings_panel->push_meta(w.line - 1); + warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); + warnings_panel->add_text(TTR("Line") + " " + itos(w.line)); + warnings_panel->add_text(" (" + w.string_code + "):"); + warnings_panel->pop(); // Color + warnings_panel->pop(); // Meta goto + warnings_panel->pop(); // Cell + + 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->add_newline(); + } + warnings_panel->pop(); // Table + line--; bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true); bool last_is_safe = false; @@ -878,98 +745,15 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_MOVE_LINE_UP: { - Ref<Script> scr = script; - if (scr.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); + code_editor->move_lines_up(); } break; case EDIT_MOVE_LINE_DOWN: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->unfold_line(line_id); - tx->unfold_line(next_id); - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + code_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -977,7 +761,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_INDENT_RIGHT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -985,72 +769,11 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_DELETE_LINE: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int to_line = tx->get_selection_to_line(); - int from_line = tx->get_selection_from_line(); - int count = Math::abs(to_line - from_line) + 1; - while (count) { - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - count--; - if (count) - tx->unfold_line(from_line); - } - tx->cursor_set_line(from_line - 1); - tx->deselect(); - } else { - int line = tx->cursor_get_line(); - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - tx->unfold_line(line); - tx->cursor_set_line(line); - } - tx->end_complex_operation(); + code_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - Ref<Script> scr = get_edited_script(); - if (scr.is_null()) - return; - - int from_line = tx->cursor_get_line(); - int to_line = tx->cursor_get_line(); - int column = tx->cursor_get_column(); - - if (tx->is_selection_active()) { - from_line = tx->get_selection_from_line(); - to_line = tx->get_selection_to_line(); - column = tx->cursor_get_column(); - } - int next_line = to_line + 1; - - if (to_line >= tx->get_line_count() - 1) { - tx->set_line(to_line, tx->get_line(to_line) + "\n"); - } - - tx->begin_complex_operation(); - for (int i = from_line; i <= to_line; i++) { - - tx->unfold_line(i); - if (i >= tx->get_line_count() - 1) { - tx->set_line(i, tx->get_line(i) + "\n"); - } - String line_clone = tx->get_line(i); - tx->insert_at(line_clone, next_line); - next_line++; - } - - tx->cursor_set_column(column); - if (tx->is_selection_active()) { - tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column()); - } - - tx->end_complex_operation(); - tx->update(); + code_editor->code_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { @@ -1069,7 +792,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TOGGLE_COMMENT: { - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -1137,7 +860,7 @@ void ScriptTextEditor::_edit_option(int p_op) { case EDIT_AUTO_INDENT: { String text = tx->get_text(); - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; @@ -1180,15 +903,15 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TO_UPPERCASE: { - _convert_case(UPPER); + _convert_case(CodeTextEditor::UPPER); } break; case EDIT_TO_LOWERCASE: { - _convert_case(LOWER); + _convert_case(CodeTextEditor::LOWER); } break; case EDIT_CAPITALIZE: { - _convert_case(CAPITALIZE); + _convert_case(CodeTextEditor::CAPITALIZE); } break; case SEARCH_FIND: { @@ -1228,7 +951,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = tx->cursor_get_line(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak); + ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } break; case DEBUG_REMOVE_ALL_BREAKPOINTS: { @@ -1239,7 +962,7 @@ void ScriptTextEditor::_edit_option(int p_op) { int line = E->get(); bool dobreak = !tx->is_line_set_as_breakpoint(line); tx->set_line_as_breakpoint(line, dobreak); - ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak); + ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } } case DEBUG_GOTO_NEXT_BREAKPOINT: { @@ -1293,7 +1016,6 @@ void ScriptTextEditor::_edit_option(int p_op) { } } break; - case HELP_CONTEXTUAL: { String text = tx->get_selection_text(); @@ -1303,6 +1025,15 @@ void ScriptTextEditor::_edit_option(int p_op) { emit_signal("request_help_search", text); } } break; + case LOOKUP_SYMBOL: { + + String text = tx->get_word_under_cursor(); + if (text == "") + text = tx->get_selection_text(); + if (text != "") { + _lookup_symbol(text, tx->cursor_get_line(), tx->cursor_get_column()); + } + } break; } } @@ -1340,6 +1071,8 @@ void ScriptTextEditor::_bind_methods() { ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line); ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol); ClassDB::bind_method("_text_edit_gui_input", &ScriptTextEditor::_text_edit_gui_input); + ClassDB::bind_method("_toggle_warning_pannel", &ScriptTextEditor::_toggle_warning_pannel); + ClassDB::bind_method("_warning_clicked", &ScriptTextEditor::_warning_clicked); ClassDB::bind_method("_color_changed", &ScriptTextEditor::_color_changed); ClassDB::bind_method("get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw); @@ -1359,7 +1092,7 @@ void ScriptTextEditor::clear_edit_menu() { void ScriptTextEditor::reload(bool p_soft) { TextEdit *te = code_editor->get_text_edit(); - Ref<Script> scr = get_edited_script(); + Ref<Script> scr = script; if (scr.is_null()) return; scr->set_source_code(te->get_text()); @@ -1508,19 +1241,13 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_RIGHT) { - + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { int col, row; TextEdit *tx = code_editor->get_text_edit(); tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); Vector2 mpos = mb->get_global_position() - tx->get_global_position(); tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); - bool has_color = (tx->get_word_at_pos(mpos) == "Color"); - int fold_state = 0; - bool can_fold = tx->can_fold(row); - bool is_folded = tx->is_folded(row); - if (tx->is_right_click_moving_caret()) { if (tx->is_selection_active()) { @@ -1540,38 +1267,62 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { } } - if (!mb->is_pressed()) { - if (has_color) { - String line = tx->get_line(row); - color_line = row; - int begin = 0; - int end = 0; - bool valid = false; - for (int i = col; i < line.length(); i++) { - if (line[i] == '(') { - begin = i; - continue; - } else if (line[i] == ')') { - end = i + 1; - valid = true; - break; - } + String word_at_mouse = tx->get_word_at_pos(mpos); + if (word_at_mouse == "") + word_at_mouse = tx->get_word_under_cursor(); + if (word_at_mouse == "") + word_at_mouse = tx->get_selection_text(); + + bool has_color = (word_at_mouse == "Color"); + int fold_state = 0; + bool foldable = tx->can_fold(row) || tx->is_folded(row); + bool open_docs = false; + bool goto_definition = false; + + if (word_at_mouse.is_resource_file()) { + open_docs = true; + } else { + + Node *base = get_tree()->get_edited_scene_root(); + if (base) { + base = _find_node_for_script(base, base, script); + } + ScriptLanguage::LookupResult result; + if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_mouse, script->get_path().get_base_dir(), base, result) == OK) { + open_docs = true; + } + } + + if (has_color) { + String line = tx->get_line(row); + color_line = row; + int begin = 0; + int end = 0; + bool valid = false; + for (int i = col; i < line.length(); i++) { + if (line[i] == '(') { + begin = i; + continue; + } else if (line[i] == ')') { + end = i + 1; + valid = true; + break; } - if (valid) { - color_args = line.substr(begin, end - begin); - String stripped = color_args.replace(" ", "").replace("(", "").replace(")", ""); - Vector<float> color = stripped.split_floats(","); - if (color.size() > 2) { - float alpha = color.size() > 3 ? color[3] : 1.0f; - color_picker->set_pick_color(Color(color[0], color[1], color[2], alpha)); - } - color_panel->set_position(get_global_transform().xform(get_local_mouse_position())); - } else { - has_color = false; + } + if (valid) { + color_args = line.substr(begin, end - begin); + String stripped = color_args.replace(" ", "").replace("(", "").replace(")", ""); + Vector<float> color = stripped.split_floats(","); + if (color.size() > 2) { + float alpha = color.size() > 3 ? color[3] : 1.0f; + color_picker->set_pick_color(Color(color[0], color[1], color[2], alpha)); } + color_panel->set_position(get_global_transform().xform(get_local_mouse_position())); + } else { + has_color = false; } - _make_context_menu(tx->is_selection_active(), has_color, can_fold, is_folded); } + _make_context_menu(tx->is_selection_active(), has_color, foldable, open_docs, goto_definition); } } } @@ -1590,7 +1341,7 @@ void ScriptTextEditor::_color_changed(const Color &p_color) { code_editor->get_text_edit()->set_line(color_line, new_line); } -void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded) { +void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition) { context_menu->clear(); if (p_selection) { @@ -1613,13 +1364,17 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); } - if (p_can_fold || p_is_folded) + if (p_foldable) context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); - if (p_color) { + if (p_color || p_open_docs || p_goto_definition) { context_menu->add_separator(); - context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR); + if (p_open_docs) + context_menu->add_item(TTR("Lookup Symbol"), LOOKUP_SYMBOL); + if (p_color) + context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR); } + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); context_menu->set_size(Vector2(1, 1)); context_menu->popup(); @@ -1629,8 +1384,13 @@ ScriptTextEditor::ScriptTextEditor() { theme_loaded = false; + VSplitContainer *editor_box = memnew(VSplitContainer); + add_child(editor_box); + editor_box->set_anchors_and_margins_preset(Control::PRESET_WIDE); + editor_box->set_v_size_flags(SIZE_EXPAND_FILL); + code_editor = memnew(CodeTextEditor); - add_child(code_editor); + editor_box->add_child(code_editor); code_editor->add_constant_override("separation", 0); code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); code_editor->connect("validate_script", this, "_validate_script"); @@ -1638,7 +1398,20 @@ ScriptTextEditor::ScriptTextEditor() { code_editor->set_code_complete_func(_code_complete_scripts, this); code_editor->get_text_edit()->connect("breakpoint_toggled", this, "_breakpoint_toggled"); code_editor->get_text_edit()->connect("symbol_lookup", this, "_lookup_symbol"); - code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + code_editor->set_v_size_flags(SIZE_EXPAND_FILL); + + warnings_panel = memnew(RichTextLabel); + editor_box->add_child(warnings_panel); + 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); + warnings_panel->set_selection_enabled(true); + warnings_panel->set_focus_mode(FOCUS_CLICK); + warnings_panel->hide(); + + code_editor->get_warning_label()->connect("gui_input", this, "_toggle_warning_pannel"); + code_editor->get_warning_count_label()->connect("gui_input", this, "_toggle_warning_pannel"); + warnings_panel->connect("meta_clicked", this, "_warning_clicked"); update_settings(); @@ -1653,6 +1426,7 @@ ScriptTextEditor::ScriptTextEditor() { context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_edit_option"); + context_menu->set_hide_on_window_lose_focus(true); color_panel = memnew(PopupPanel); add_child(color_panel); @@ -1664,6 +1438,7 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu = memnew(MenuButton); edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); @@ -1717,6 +1492,7 @@ ScriptTextEditor::ScriptTextEditor() { search_menu = memnew(MenuButton); edit_hb->add_child(search_menu); search_menu->set_text(TTR("Search")); + search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); @@ -1743,9 +1519,12 @@ ScriptTextEditor::ScriptTextEditor() { code_editor->get_text_edit()->set_drag_forwarding(this); } -static ScriptEditorBase *create_editor(const Ref<Script> &p_script) { +static ScriptEditorBase *create_editor(const RES &p_resource) { - return memnew(ScriptTextEditor); + if (Object::cast_to<Script>(*p_resource)) { + return memnew(ScriptTextEditor); + } + return NULL; } void ScriptTextEditor::register_editor() { diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index a415f478e8..837201a947 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -39,6 +39,7 @@ class ScriptTextEditor : public ScriptEditorBase { GDCLASS(ScriptTextEditor, ScriptEditorBase); CodeTextEditor *code_editor; + RichTextLabel *warnings_panel; Ref<Script> script; @@ -112,6 +113,7 @@ class ScriptTextEditor : public ScriptEditorBase { DEBUG_GOTO_NEXT_BREAKPOINT, DEBUG_GOTO_PREV_BREAKPOINT, HELP_CONTEXTUAL, + LOOKUP_SYMBOL, }; protected: @@ -123,6 +125,8 @@ protected: void _code_complete_script(const String &p_code, List<String> *r_options, bool &r_force); void _load_theme_settings(); void _set_theme_for_script(); + void _toggle_warning_pannel(const Ref<InputEvent> &p_event); + void _warning_clicked(Variant p_line); void _notification(int p_what); static void _bind_methods(); @@ -131,19 +135,14 @@ protected: void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); - void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded); + void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition); void _text_edit_gui_input(const Ref<InputEvent> &ev); void _color_changed(const Color &p_color); void _goto_line(int p_line) { goto_line(p_line); } void _lookup_symbol(const String &p_symbol, int p_row, int p_column); - enum CaseStyle { - UPPER, - LOWER, - CAPITALIZE, - }; - void _convert_case(CaseStyle p_case); + void _convert_case(CodeTextEditor::CaseStyle p_case); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; @@ -154,14 +153,13 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void apply_code(); - virtual Ref<Script> get_edited_script() const; + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); virtual Vector<String> get_functions(); - virtual void set_edited_script(const Ref<Script> &p_script); virtual void reload_text(); virtual String get_name(); virtual Ref<Texture> get_icon(); virtual bool is_unsaved(); - virtual Variant get_edit_state(); virtual void set_edit_state(const Variant &p_state); virtual void ensure_focus(); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 4b7f27c0c1..ea1876c27a 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -258,84 +258,10 @@ void ShaderEditor::_menu_option(int p_option) { shader_editor->get_text_edit()->select_all(); } break; case EDIT_MOVE_LINE_UP: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = from_line; i <= to_line; i++) { - int line_id = i; - int next_id = i - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_up = from_line > 0 ? from_line - 1 : from_line; - int to_line_up = to_line > 0 ? to_line - 1 : to_line; - tx->select(from_line_up, from_col, to_line_up, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id - 1; - - if (line_id == 0 || next_id < 0) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + shader_editor->move_lines_up(); } break; case EDIT_MOVE_LINE_DOWN: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int from_line = tx->get_selection_from_line(); - int from_col = tx->get_selection_from_column(); - int to_line = tx->get_selection_to_line(); - int to_column = tx->get_selection_to_column(); - - for (int i = to_line; i >= from_line; i--) { - int line_id = i; - int next_id = i + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; - int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; - tx->select(from_line_down, from_col, to_line_down, to_column); - } else { - int line_id = tx->cursor_get_line(); - int next_id = line_id + 1; - - if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) - return; - - tx->swap_lines(line_id, next_id); - tx->cursor_set_line(next_id); - } - tx->end_complex_operation(); - tx->update(); - + shader_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { @@ -356,55 +282,10 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_DELETE_LINE: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - tx->begin_complex_operation(); - int line = tx->cursor_get_line(); - tx->set_line(tx->cursor_get_line(), ""); - tx->backspace_at_cursor(); - tx->cursor_set_line(line); - tx->end_complex_operation(); - + shader_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - - TextEdit *tx = shader_editor->get_text_edit(); - if (shader.is_null()) - return; - - int from_line = tx->cursor_get_line(); - int to_line = tx->cursor_get_line(); - int column = tx->cursor_get_column(); - - if (tx->is_selection_active()) { - from_line = tx->get_selection_from_line(); - to_line = tx->get_selection_to_line(); - column = tx->cursor_get_column(); - } - int next_line = to_line + 1; - - tx->begin_complex_operation(); - for (int i = from_line; i <= to_line; i++) { - - if (i >= tx->get_line_count() - 1) { - tx->set_line(i, tx->get_line(i) + "\n"); - } - String line_clone = tx->get_line(i); - tx->insert_at(line_clone, next_line); - next_line++; - } - - tx->cursor_set_column(column); - if (tx->is_selection_active()) { - tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column()); - } - - tx->end_complex_operation(); - tx->update(); - + shader_editor->code_lines_down(); } break; case EDIT_TOGGLE_COMMENT: { @@ -584,7 +465,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (mb.is_valid()) { - if (mb->get_button_index() == BUTTON_RIGHT) { + if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { int col, row; TextEdit *tx = shader_editor->get_text_edit(); @@ -610,10 +491,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { tx->cursor_set_column(col); } } - - if (!mb->is_pressed()) { - _make_context_menu(tx->is_selection_active()); - } + _make_context_menu(tx->is_selection_active()); } } } @@ -665,6 +543,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { context_menu = memnew(PopupMenu); add_child(context_menu); context_menu->connect("id_pressed", this, "_menu_option"); + context_menu->set_hide_on_window_lose_focus(true); VBoxContainer *main_container = memnew(VBoxContainer); HBoxContainer *hbc = memnew(HBoxContainer); @@ -673,6 +552,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { //edit_menu->set_position(Point2(5, -1)); edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->set_hide_on_window_lose_focus(true); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); @@ -697,7 +577,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { search_menu = memnew(MenuButton); //search_menu->set_position(Point2(38, -1)); search_menu->set_text(TTR("Search")); - + search_menu->get_popup()->set_hide_on_window_lose_focus(true); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index 40a696119e..fe7d1df50c 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -68,16 +68,16 @@ void SkeletonEditor::create_physical_skeleton() { if (parent < 0) { - bones_infos[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); + bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); } else { - bones_infos[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); + bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); /// create physical bone on parent if (!bones_infos[parent].physical_bone) { - bones_infos[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos); + bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos); ur->create_action(TTR("Create physical bones")); ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone); diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp new file mode 100644 index 0000000000..2d343d3edd --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_ik_editor_plugin.h" + +#include "scene/animation/skeleton_ik.h" + +void SkeletonIKEditorPlugin::_play() { + + if (!skeleton_ik) + return; + + if (!skeleton_ik->get_parent_skeleton()) + return; + + if (play_btn->is_pressed()) { + + initial_bone_poses.resize(skeleton_ik->get_parent_skeleton()->get_bone_count()); + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + initial_bone_poses.write[i] = skeleton_ik->get_parent_skeleton()->get_bone_pose(i); + } + + skeleton_ik->start(); + } else { + skeleton_ik->stop(); + + if (initial_bone_poses.size() != skeleton_ik->get_parent_skeleton()->get_bone_count()) + return; + + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + skeleton_ik->get_parent_skeleton()->set_bone_pose(i, initial_bone_poses[i]); + } + } +} + +void SkeletonIKEditorPlugin::edit(Object *p_object) { + + if (p_object != skeleton_ik) { + if (skeleton_ik) { + play_btn->set_pressed(false); + _play(); + } + } + + SkeletonIK *s = Object::cast_to<SkeletonIK>(p_object); + if (!s) + return; + + skeleton_ik = s; +} + +bool SkeletonIKEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("SkeletonIK"); +} + +void SkeletonIKEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) + play_btn->show(); + else + play_btn->hide(); +} + +void SkeletonIKEditorPlugin::_bind_methods() { + + ClassDB::bind_method("_play", &SkeletonIKEditorPlugin::_play); +} + +SkeletonIKEditorPlugin::SkeletonIKEditorPlugin(EditorNode *p_node) { + + editor = p_node; + play_btn = memnew(Button); + play_btn->set_icon(editor->get_gui_base()->get_icon("Play", "EditorIcons")); + play_btn->set_text(TTR("Play IK")); + play_btn->set_toggle_mode(true); + play_btn->hide(); + play_btn->connect("pressed", this, "_play"); + add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); + skeleton_ik = NULL; +} + +SkeletonIKEditorPlugin::~SkeletonIKEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h new file mode 100644 index 0000000000..e645bea39a --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_EDITOR_PLUGIN_H +#define SKELETON_IK_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" + +class SkeletonIK; + +class SkeletonIKEditorPlugin : public EditorPlugin { + + GDCLASS(SkeletonIKEditorPlugin, EditorPlugin); + + SkeletonIK *skeleton_ik; + + Button *play_btn; + EditorNode *editor; + Vector<Transform> initial_bone_poses; + + void _play(); + +protected: + static void _bind_methods(); + +public: + virtual String get_name() const { return "SkeletonIK"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + SkeletonIKEditorPlugin(EditorNode *p_node); + ~SkeletonIKEditorPlugin(); +}; + +#endif // SKELETON_IK_EDITOR_PLUGIN_H diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 37b8562e96..40d6b67426 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -184,49 +184,6 @@ Transform SpatialEditorViewport::to_camera_transform(const Cursor &p_cursor) con return camera_transform; } -String SpatialEditorGizmo::get_handle_name(int p_idx) const { - - if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) - return get_script_instance()->call("get_handle_name", p_idx); - - return ""; -} - -Variant SpatialEditorGizmo::get_handle_value(int p_idx) const { - - if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) - return get_script_instance()->call("get_handle_value", p_idx); - - return Variant(); -} - -void SpatialEditorGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { - - if (get_script_instance() && get_script_instance()->has_method("set_handle")) - get_script_instance()->call("set_handle", p_idx, p_camera, p_point); -} - -void SpatialEditorGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { - - if (get_script_instance() && get_script_instance()->has_method("commit_handle")) - get_script_instance()->call("commit_handle", p_idx, p_restore, p_cancel); -} - -bool SpatialEditorGizmo::intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum) { - - return false; -} - -bool SpatialEditorGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) { - - return false; -} - -SpatialEditorGizmo::SpatialEditorGizmo() { - - selected = false; -} - int SpatialEditorViewport::get_selected_count() const { Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -346,7 +303,7 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, Vector3 pos = _get_ray_pos(p_pos); Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); - Set<Ref<SpatialEditorGizmo> > found_gizmos; + Set<Ref<EditorSpatialGizmo> > found_gizmos; Node *edited_scene = get_tree()->get_edited_scene_root(); ObjectID closest = 0; @@ -361,7 +318,7 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, if (!spat) continue; - Ref<SpatialEditorGizmo> seg = spat->get_gizmo(); + Ref<EditorSpatialGizmo> seg = spat->get_gizmo(); if ((!seg.is_valid()) || found_gizmos.has(seg)) { continue; @@ -418,7 +375,7 @@ void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_incl Vector3 pos = _get_ray_pos(p_pos); Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario()); - Set<Ref<SpatialEditorGizmo> > found_gizmos; + Set<Ref<EditorSpatialGizmo> > found_gizmos; r_includes_current = false; @@ -429,7 +386,7 @@ void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_incl if (!spat) continue; - Ref<SpatialEditorGizmo> seg = spat->get_gizmo(); + Ref<EditorSpatialGizmo> seg = spat->get_gizmo(); if (!seg.is_valid()) continue; @@ -559,7 +516,7 @@ void SpatialEditorViewport::_select_region() { if (selected.find(root_sp) != -1) continue; - Ref<SpatialEditorGizmo> seg = sp->get_gizmo(); + Ref<EditorSpatialGizmo> seg = sp->get_gizmo(); if (!seg.is_valid()) continue; @@ -963,7 +920,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (b->is_pressed() && _edit.gizmo.is_valid()) { //restore _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, true); - _edit.gizmo = Ref<SpatialEditorGizmo>(); + _edit.gizmo = Ref<EditorSpatialGizmo>(); } if (_edit.mode == TRANSFORM_NONE && b->is_pressed()) { @@ -1079,7 +1036,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (can_select_gizmos && spatial_editor->get_selected()) { - Ref<SpatialEditorGizmo> seg = spatial_editor->get_selected()->get_gizmo(); + Ref<EditorSpatialGizmo> seg = spatial_editor->get_selected()->get_gizmo(); if (seg.is_valid()) { int handle = -1; Vector3 point; @@ -1158,7 +1115,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { Spatial *spa = Object::cast_to<Spatial>(ObjectDB::get_instance(clicked)); if (spa) { - Ref<SpatialEditorGizmo> seg = spa->get_gizmo(); + Ref<EditorSpatialGizmo> seg = spa->get_gizmo(); if (seg.is_valid()) { _edit.gizmo = seg; @@ -1175,7 +1132,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (_edit.gizmo.is_valid()) { _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, false); - _edit.gizmo = Ref<SpatialEditorGizmo>(); + _edit.gizmo = Ref<EditorSpatialGizmo>(); break; } if (clicked) { @@ -1233,7 +1190,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (spatial_editor->get_selected()) { - Ref<SpatialEditorGizmo> seg = spatial_editor->get_selected()->get_gizmo(); + Ref<EditorSpatialGizmo> seg = spatial_editor->get_selected()->get_gizmo(); if (seg.is_valid()) { int selected_handle = -1; @@ -1928,6 +1885,11 @@ void SpatialEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, cons void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { + if (lock_rotation) { + _nav_pan(p_event, p_relative); + return; + } + real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); @@ -2151,6 +2113,21 @@ void SpatialEditorViewport::_notification(int p_what) { _update_freelook(delta); + Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root(); + if (previewing_cinema == true && scene_root != NULL) { + Camera *cam = scene_root->get_viewport()->get_camera(); + if (cam != NULL && cam != previewing) { + //then switch the viewport's camera to the scene's viewport camera + if (previewing != NULL) { + previewing->disconnect("tree_exited", this, "_preview_exited_scene"); + } + previewing = cam; + previewing->connect("tree_exited", this, "_preview_exited_scene"); + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), cam->get_camera()); + surface->update(); + } + } + _update_camera(delta); Map<Node *, Object *> &selection = editor_selection->get_selection(); @@ -2256,6 +2233,13 @@ void SpatialEditorViewport::_notification(int p_what) { text += TTR("FPS") + ": " + itos(temp_fps) + " (" + String::num(1000.0f / temp_fps, 2) + " ms)"; fps_label->set_text(text); } + + bool show_cinema = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); + cinema_label->set_visible(show_cinema); + if (show_cinema) { + float cinema_half_width = cinema_label->get_size().width / 2.0f; + cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width); + } } if (p_what == NOTIFICATION_ENTER_TREE) { @@ -2268,6 +2252,7 @@ void SpatialEditorViewport::_notification(int p_what) { surface->connect("focus_exited", this, "_surface_focus_exit"); info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); preview_camera->set_icon(get_icon("Camera", "EditorIcons")); _init_gizmo_instance(index); } @@ -2563,6 +2548,19 @@ void SpatialEditorViewport::_menu_option(int p_option) { _update_name(); } break; + case VIEW_LOCK_ROTATION: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); + bool current = view_menu->get_popup()->is_item_checked(idx); + lock_rotation = !current; + view_menu->get_popup()->set_item_checked(idx, !current); + if (lock_rotation) { + view_menu->set_icon(get_icon("Lock", "EditorIcons")); + } else { + view_menu->set_icon(Ref<Texture>()); + } + + } break; case VIEW_AUDIO_LISTENER: { int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER); @@ -2581,6 +2579,22 @@ void SpatialEditorViewport::_menu_option(int p_option) { view_menu->get_popup()->set_item_checked(idx, current); } break; + case VIEW_CINEMATIC_PREVIEW: { + + int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); + bool current = view_menu->get_popup()->is_item_checked(idx); + current = !current; + view_menu->get_popup()->set_item_checked(idx, current); + previewing_cinema = true; + _toggle_cinema_preview(current); + + if (current) { + preview_camera->hide(); + } else { + if (previewing != NULL) + preview_camera->show(); + } + } break; case VIEW_GIZMOS: { int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS); @@ -2744,6 +2758,25 @@ void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) { } } +void SpatialEditorViewport::_toggle_cinema_preview(bool p_activate) { + previewing_cinema = p_activate; + if (!previewing_cinema) { + if (previewing != NULL) + previewing->disconnect("tree_exited", this, "_preview_exited_scene"); + + previewing = NULL; + VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore + preview_camera->set_pressed(false); + if (!preview) { + preview_camera->hide(); + } else { + preview_camera->show(); + } + view_menu->show(); + surface->update(); + } +} + void SpatialEditorViewport::_selection_result_pressed(int p_result) { if (selection_results.size() <= p_result) @@ -2768,7 +2801,7 @@ void SpatialEditorViewport::set_can_preview(Camera *p_preview) { preview = p_preview; - if (!preview_camera->is_pressed()) + if (!preview_camera->is_pressed() && !previewing_cinema) preview_camera->set_visible(p_preview); } @@ -2836,6 +2869,12 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { if (!view_menu->get_popup()->is_item_checked(idx)) _menu_option(display); } + if (p_state.has("lock_rotation")) { + lock_rotation = p_state["lock_rotation"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION); + view_menu->get_popup()->set_item_checked(idx, lock_rotation); + } if (p_state.has("use_environment")) { bool env = p_state["use_environment"]; @@ -2883,6 +2922,12 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION); view_menu->get_popup()->set_item_checked(idx, half_res); } + if (p_state.has("cinematic_preview")) { + previewing_cinema = p_state["cinematic_preview"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW); + view_menu->get_popup()->set_item_checked(idx, previewing_cinema); + } if (p_state.has("previewing")) { Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]); @@ -2921,8 +2966,11 @@ Dictionary SpatialEditorViewport::get_state() const { d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); d["half_res"] = viewport_container->get_stretch_shrink() > 1; + d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); if (previewing) d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing); + if (lock_rotation) + d["lock_rotation"] = lock_rotation; return d; } @@ -2951,6 +2999,7 @@ void SpatialEditorViewport::_bind_methods() { void SpatialEditorViewport::reset() { orthogonal = false; + lock_rotation = false; message_time = 0; message = ""; last_message = ""; @@ -3007,7 +3056,7 @@ Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const Vector3 world_pos = _get_ray_pos(p_pos); Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario()); - Set<Ref<SpatialEditorGizmo> > found_gizmos; + Set<Ref<EditorSpatialGizmo> > found_gizmos; float closest_dist = MAX_DISTANCE; @@ -3021,7 +3070,7 @@ Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const if (!mesh_instance) continue; - Ref<SpatialEditorGizmo> seg = mesh_instance->get_gizmo(); + Ref<EditorSpatialGizmo> seg = mesh_instance->get_gizmo(); if ((!seg.is_valid()) || found_gizmos.has(seg)) { continue; @@ -3216,7 +3265,7 @@ void SpatialEditorViewport::_perform_drop_data() { files_str += error_files[i].get_file().get_basename() + ","; } files_str = files_str.substr(0, files_str.length() - 1); - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str())); accept->popup_centered_minsize(); } @@ -3305,7 +3354,7 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p } } if (list.size() != 1) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3377,6 +3426,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true); view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION); + view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL); view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME); view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW); @@ -3396,6 +3447,9 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true); view_menu->get_popup()->add_separator(); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_cinematic_preview", TTR("Cinematic Preview")), VIEW_CINEMATIC_PREVIEW); + + view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_selection_with_view"), VIEW_ALIGN_SELECTION_WITH_VIEW); @@ -3445,6 +3499,15 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed surface->add_child(fps_label); fps_label->hide(); + cinema_label = memnew(Label); + cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); + cinema_label->set_h_grow_direction(GROW_DIRECTION_END); + cinema_label->set_align(Label::ALIGN_CENTER); + surface->add_child(cinema_label); + cinema_label->set_text(TTR("Cinematic Preview")); + cinema_label->hide(); + previewing_cinema = false; + accept = NULL; freelook_active = false; @@ -3786,10 +3849,6 @@ void SpatialEditor::select_gizmo_highlight_axis(int p_axis) { } } -int SpatialEditor::get_skeleton_visibility_state() const { - return view_menu->get_popup()->get_item_state(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON)); -} - void SpatialEditor::update_transform_gizmo() { List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -3942,6 +4001,16 @@ Dictionary SpatialEditor::get_state() const { d["znear"] = get_znear(); d["zfar"] = get_zfar(); + Dictionary gizmos_status; + for (int i = 0; i < gizmo_plugins.size(); i++) { + if (!gizmo_plugins[i]->can_be_hidden()) continue; + int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); + String name = gizmo_plugins[i]->get_name(); + gizmos_status[name] = state; + } + + d["gizmos_status"] = gizmos_status; + return d; } void SpatialEditor::set_state(const Dictionary &p_state) { @@ -4015,6 +4084,39 @@ void SpatialEditor::set_state(const Dictionary &p_state) { VisualServer::get_singleton()->instance_set_visible(origin_instance, use); } } + + if (d.has("gizmos_status")) { + Dictionary gizmos_status = d["gizmos_status"]; + List<Variant> keys; + gizmos_status.get_key_list(&keys); + + for (int j = 0; j < gizmo_plugins.size(); ++j) { + if (!gizmo_plugins[j]->can_be_hidden()) continue; + int state = EditorSpatialGizmoPlugin::ON_TOP; + for (uint32_t i = 0; i < keys.size(); i++) { + if (gizmo_plugins.write[j]->get_name() == keys[i]) { + state = gizmos_status[keys[i]]; + } + } + + const int idx = gizmos_menu->get_item_index(j); + + gizmos_menu->set_item_multistate(idx, state); + gizmo_plugins.write[j]->set_state(state); + + switch (state) { + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } + } + } } void SpatialEditor::edit(Spatial *p_spatial) { @@ -4022,7 +4124,7 @@ void SpatialEditor::edit(Spatial *p_spatial) { if (p_spatial != selected) { if (selected) { - Ref<SpatialEditorGizmo> seg = selected->get_gizmo(); + Ref<EditorSpatialGizmo> seg = selected->get_gizmo(); if (seg.is_valid()) { seg->set_selected(false); selected->update_gizmo(); @@ -4034,7 +4136,7 @@ void SpatialEditor::edit(Spatial *p_spatial) { if (selected) { - Ref<SpatialEditorGizmo> seg = selected->get_gizmo(); + Ref<EditorSpatialGizmo> seg = selected->get_gizmo(); if (seg.is_valid()) { seg->set_selected(true); selected->update_gizmo(); @@ -4108,6 +4210,30 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) { } } +void SpatialEditor::_menu_gizmo_toggled(int p_option) { + + const int idx = gizmos_menu->get_item_index(p_option); + gizmos_menu->toggle_item_multistate(idx); + + // Change icon + const int state = gizmos_menu->get_item_state(idx); + switch (state) { + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden")); + break; + } + + gizmo_plugins.write[p_option]->set_state(state); + + update_all_gizmos(); +} + void SpatialEditor::_menu_item_pressed(int p_option) { switch (p_option) { @@ -4241,6 +4367,9 @@ void SpatialEditor::_menu_item_pressed(int p_option) { settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50)); } break; + case MENU_SNAP_TO_FLOOR: { + snap_selected_nodes_to_floor(); + } break; case MENU_LOCK_SELECTED: { List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4279,28 +4408,6 @@ void SpatialEditor::_menu_item_pressed(int p_option) { _refresh_menu_icons(); } break; - case MENU_VISIBILITY_SKELETON: { - - const int idx = view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON); - view_menu->get_popup()->toggle_item_multistate(idx); - - // Change icon - const int state = view_menu->get_popup()->get_item_state(idx); - switch (state) { - case 0: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden")); - break; - case 1: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible")); - break; - case 2: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray")); - break; - } - - update_all_gizmos(); - - } break; } } @@ -4616,6 +4723,26 @@ void SpatialEditor::_init_indicators() { _generate_selection_box(); } +struct _GizmoPluginComparator { + + bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { + return p_a->get_name() < p_b->get_name(); + } +}; + +void SpatialEditor::_init_gizmos_menu() { + _register_all_gizmos(); + + gizmo_plugins.sort_custom<_GizmoPluginComparator>(); + + for (int i = 0; i < gizmo_plugins.size(); ++i) { + if (!gizmo_plugins[i]->can_be_hidden()) continue; + String plugin_name = gizmo_plugins[i]->get_name(); + gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::ON_TOP, i); + gizmos_menu->set_item_icon(gizmos_menu->get_item_index(i), gizmos_menu->get_icon("visibility_visible")); + } +} + void SpatialEditor::_init_grid() { PoolVector<Color> grid_colors[3]; @@ -4641,9 +4768,7 @@ void SpatialEditor::_init_grid() { Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); Color line_color = secondary_grid_color; - if (j == 0) { - continue; - } else if (j % primary_grid_steps == 0) { + if (j % primary_grid_steps == 0) { line_color = primary_grid_color; } @@ -4718,6 +4843,119 @@ void SpatialEditor::_refresh_menu_icons() { tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked); } +template <typename T> +Set<T *> _get_child_nodes(Node *parent_node) { + Set<T *> nodes = Set<T *>(); + T *node = Node::cast_to<T>(parent_node); + if (node) { + nodes.insert(node); + } + + for (int i = 0; i < parent_node->get_child_count(); i++) { + Node *child_node = parent_node->get_child(i); + Set<T *> child_nodes = _get_child_nodes<T>(child_node); + for (typename Set<T *>::Element *I = child_nodes.front(); I; I = I->next()) { + nodes.insert(I->get()); + } + } + + return nodes; +} + +Set<RID> _get_physics_bodies_rid(Node *node) { + Set<RID> rids = Set<RID>(); + PhysicsBody *pb = Node::cast_to<PhysicsBody>(node); + if (pb) { + rids.insert(pb->get_rid()); + } + Set<PhysicsBody *> child_nodes = _get_child_nodes<PhysicsBody>(node); + for (Set<PhysicsBody *>::Element *I = child_nodes.front(); I; I = I->next()) { + rids.insert(I->get()->get_rid()); + } + + return rids; +} + +void SpatialEditor::snap_selected_nodes_to_floor() { + List<Node *> &selection = editor_selection->get_selected_node_list(); + Dictionary snap_data; + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Spatial *sp = Object::cast_to<Spatial>(E->get()); + if (sp) { + Vector3 from = Vector3(); + Vector3 position_offset = Vector3(); + + // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin + Set<VisualInstance *> vi = _get_child_nodes<VisualInstance>(sp); + Set<CollisionShape *> cs = _get_child_nodes<CollisionShape>(sp); + + if (cs.size()) { + AABB aabb = sp->get_global_transform().xform(cs.front()->get()->get_shape()->get_debug_mesh()->get_aabb()); + for (Set<CollisionShape *>::Element *I = cs.front(); I; I = I->next()) { + aabb.merge_with(sp->get_global_transform().xform(I->get()->get_shape()->get_debug_mesh()->get_aabb())); + } + Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); + from = aabb.position + size; + position_offset.y = from.y - sp->get_global_transform().origin.y; + } else if (vi.size()) { + AABB aabb = vi.front()->get()->get_transformed_aabb(); + for (Set<VisualInstance *>::Element *I = vi.front(); I; I = I->next()) { + aabb.merge_with(I->get()->get_transformed_aabb()); + } + Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); + from = aabb.position + size; + position_offset.y = from.y - sp->get_global_transform().origin.y; + } else { + from = sp->get_global_transform().origin; + } + + // We add a bit of margin to the from position to avoid it from snapping + // when the spatial is already on a floor and there's another floor under + // it + from = from + Vector3(0.0, 0.1, 0.0); + + Dictionary d; + + d["from"] = from; + d["position_offset"] = position_offset; + snap_data[sp] = d; + } + } + + PhysicsDirectSpaceState *ss = get_tree()->get_root()->get_world()->get_direct_space_state(); + PhysicsDirectSpaceState::RayResult result; + + Array keys = snap_data.keys(); + + if (keys.size()) { + undo_redo->create_action("Snap Nodes To Floor"); + + for (int i = 0; i < keys.size(); i++) { + Node *node = keys[i]; + Spatial *sp = Object::cast_to<Spatial>(node); + + Dictionary d = snap_data[node]; + Vector3 from = d["from"]; + Vector3 position_offset = d["position_offset"]; + + Vector3 to = from - Vector3(0.0, 10.0, 0.0); + Set<RID> excluded = _get_physics_bodies_rid(sp); + + if (ss->intersect_ray(from, to, result, excluded)) { + Transform new_transform = sp->get_global_transform(); + new_transform.origin.y = result.position.y; + new_transform.origin = new_transform.origin - position_offset; + + undo_redo->add_do_method(sp, "set_global_transform", new_transform); + undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform()); + } + } + + undo_redo->commit_action(); + } +} + void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) { if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack()) @@ -4746,6 +4984,8 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) { else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event)) _menu_item_pressed(MENU_TOOL_SCALE); + else if (ED_IS_SHORTCUT("spatial_editor/snap_to_floor", p_event)) + snap_selected_nodes_to_floor(); else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event)) if (are_local_coords_enabled()) { @@ -4783,7 +5023,6 @@ void SpatialEditor::_notification(int p_what) { view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON), view_menu->get_popup()->get_icon("visibility_visible")); _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); @@ -4796,14 +5035,13 @@ void SpatialEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - gizmos = memnew(SpatialEditorGizmos); + _init_gizmos_menu(); _init_indicators(); } if (p_what == NOTIFICATION_EXIT_TREE) { _finish_indicators(); - memdelete(gizmos); } if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); @@ -4862,25 +5100,21 @@ void SpatialEditor::_request_gizmo(Object *p_obj) { return; if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_a_parent_of(sp)))) { - Ref<SpatialEditorGizmo> seg; + Ref<EditorSpatialGizmo> seg; - for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_editor_plugin_count(); i++) { + for (int i = 0; i < gizmo_plugins.size(); ++i) { + seg = gizmo_plugins.write[i]->get_gizmo(sp); - seg = EditorNode::get_singleton()->get_editor_data().get_editor_plugin(i)->create_spatial_gizmo(sp); - if (seg.is_valid()) - break; - } + if (seg.is_valid()) { + sp->set_gizmo(seg); - if (!seg.is_valid()) { - seg = gizmos->get_gizmo(sp); - } - if (seg.is_valid()) { - sp->set_gizmo(seg); - } + if (sp == selected) { + seg->set_selected(true); + selected->update_gizmo(); + } - if (seg.is_valid() && sp == selected) { - seg->set_selected(true); - selected->update_gizmo(); + break; + } } } } @@ -4936,11 +5170,35 @@ void SpatialEditor::_node_removed(Node *p_node) { selected = NULL; } +void SpatialEditor::_register_all_gizmos() { + register_gizmo_plugin(Ref<CameraSpatialGizmoPlugin>(memnew(CameraSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<LightSpatialGizmoPlugin>(memnew(LightSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<AudioStreamPlayer3DSpatialGizmoPlugin>(memnew(AudioStreamPlayer3DSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin))); + register_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin))); + register_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin))); + register_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin))); + register_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin))); + register_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin))); + register_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<JointSpatialGizmoPlugin>(memnew(JointSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<PhysicalBoneSpatialGizmoPlugin>(memnew(PhysicalBoneSpatialGizmoPlugin))); +} + void SpatialEditor::_bind_methods() { ClassDB::bind_method("_unhandled_key_input", &SpatialEditor::_unhandled_key_input); ClassDB::bind_method("_node_removed", &SpatialEditor::_node_removed); ClassDB::bind_method("_menu_item_pressed", &SpatialEditor::_menu_item_pressed); + ClassDB::bind_method("_menu_gizmo_toggled", &SpatialEditor::_menu_gizmo_toggled); ClassDB::bind_method("_menu_item_toggled", &SpatialEditor::_menu_item_toggled); ClassDB::bind_method("_xform_dialog_action", &SpatialEditor::_xform_dialog_action); ClassDB::bind_method("_get_editor_data", &SpatialEditor::_get_editor_data); @@ -5011,7 +5269,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true); tool_button[TOOL_MODE_SELECT]->set_flat(true); tool_button[TOOL_MODE_SELECT]->set_pressed(true); - button_binds[0] = MENU_TOOL_SELECT; + button_binds.write[0] = MENU_TOOL_SELECT; tool_button[TOOL_MODE_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SELECT]->set_tooltip(TTR("Select Mode (Q)") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection")); @@ -5019,7 +5277,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]); tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true); tool_button[TOOL_MODE_MOVE]->set_flat(true); - button_binds[0] = MENU_TOOL_MOVE; + button_binds.write[0] = MENU_TOOL_MOVE; tool_button[TOOL_MODE_MOVE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_MOVE]->set_tooltip(TTR("Move Mode (W)")); @@ -5027,7 +5285,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]); tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true); tool_button[TOOL_MODE_ROTATE]->set_flat(true); - button_binds[0] = MENU_TOOL_ROTATE; + button_binds.write[0] = MENU_TOOL_ROTATE; tool_button[TOOL_MODE_ROTATE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_ROTATE]->set_tooltip(TTR("Rotate Mode (E)")); @@ -5035,7 +5293,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]); tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true); tool_button[TOOL_MODE_SCALE]->set_flat(true); - button_binds[0] = MENU_TOOL_SCALE; + button_binds.write[0] = MENU_TOOL_SCALE; tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_SCALE]->set_tooltip(TTR("Scale Mode (R)")); @@ -5043,19 +5301,19 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]); tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true); - button_binds[0] = MENU_TOOL_LIST_SELECT; + button_binds.write[0] = MENU_TOOL_LIST_SELECT; tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode).")); tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]); - button_binds[0] = MENU_LOCK_SELECTED; + button_binds.write[0] = MENU_LOCK_SELECTED; tool_button[TOOL_LOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved).")); tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton); hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]); - button_binds[0] = MENU_UNLOCK_SELECTED; + button_binds.write[0] = MENU_UNLOCK_SELECTED; tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); @@ -5066,7 +5324,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true); tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true); - button_binds[0] = MENU_TOOL_LOCAL_COORDS; + button_binds.write[0] = MENU_TOOL_LOCAL_COORDS; tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", this, "_menu_item_toggled", button_binds); ED_SHORTCUT("spatial_editor/local_coords", TTR("Local Coords"), KEY_T); sct = ED_GET_SHORTCUT("spatial_editor/local_coords").ptr()->get_as_text(); @@ -5076,7 +5334,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]); tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true); tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true); - button_binds[0] = MENU_TOOL_USE_SNAP; + button_binds.write[0] = MENU_TOOL_USE_SNAP; tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", this, "_menu_item_toggled", button_binds); ED_SHORTCUT("spatial_editor/snap", TTR("Snap"), KEY_Y); sct = ED_GET_SHORTCUT("spatial_editor/snap").ptr()->get_as_text(); @@ -5105,6 +5363,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { ED_SHORTCUT("spatial_editor/tool_move", TTR("Tool Move"), KEY_W); ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Tool Rotate"), KEY_E); ED_SHORTCUT("spatial_editor/tool_scale", TTR("Tool Scale"), KEY_R); + ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap To Floor"), KEY_PAGEDOWN); ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F); @@ -5115,6 +5374,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { hbc_menu->add_child(transform_menu); p = transform_menu->get_popup(); + p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap object to floor")), MENU_SNAP_TO_FLOOR); p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); @@ -5139,19 +5399,26 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); p->add_separator(); + p->add_submenu_item(TTR("Gizmos"), "GizmosMenu"); + + p->add_separator(); + p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings")), MENU_VIEW_CAMERA_SETTINGS); - p->add_separator(); - p->add_multistate_item(TTR("Skeleton Gizmo visibility"), 3, 1, MENU_VISIBILITY_SKELETON); - p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); p->connect("id_pressed", this, "_menu_item_pressed"); + gizmos_menu = memnew(PopupMenu); + p->add_child(gizmos_menu); + gizmos_menu->set_name("GizmosMenu"); + gizmos_menu->set_hide_on_checkable_item_selection(false); + gizmos_menu->connect("id_pressed", this, "_menu_gizmo_toggled"); + /* REST OF MENU */ palette_split = memnew(HSplitContainer); @@ -5359,6 +5626,10 @@ void SpatialEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) { spatial_editor->snap_cursor_to_plane(p_plane); } +void SpatialEditor::register_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> ref) { + gizmo_plugins.push_back(ref); +} + SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { editor = p_node; @@ -5372,3 +5643,182 @@ SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { SpatialEditorPlugin::~SpatialEditorPlugin() { } + +void EditorSpatialGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { + + Color instanced_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instanced"); + + Vector<Ref<SpatialMaterial> > mats; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instanced = i < 2; + + Ref<SpatialMaterial> material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + + Color color = instanced ? instanced_color : p_color; + + if (!selected) { + color.a *= 0.3; + } + + material->set_albedo(color); + material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 1); + + if (p_use_vertex_color) { + material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); + } + + if (p_billboard) { + material->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); + } + + if (p_on_top && selected) { + material->set_on_top_of_alpha(); + } + + mats.push_back(material); + } + + materials[p_name] = mats; +} + +void EditorSpatialGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture> &p_texture, bool p_on_top, const Color &p_albedo) { + + Color instanced_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instanced"); + + Vector<Ref<SpatialMaterial> > icons; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instanced = i < 2; + + Ref<SpatialMaterial> icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + + Color color = instanced ? instanced_color : p_albedo; + + if (!selected) { + color.a *= 0.3; + } + + icon->set_albedo(color); + + icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); + icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); + icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, p_texture); + icon->set_flag(SpatialMaterial::FLAG_FIXED_SIZE, true); + icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); + icon->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN); + + if (p_on_top && selected) { + icon->set_on_top_of_alpha(); + } + + icons.push_back(icon); + } + + materials[p_name] = icons; +} + +void EditorSpatialGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard) { + Ref<SpatialMaterial> handle_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + + handle_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); + handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + handle_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true); + Ref<Texture> handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle", "EditorIcons"); + handle_material->set_point_size(handle_t->get_width()); + handle_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle_t); + handle_material->set_albedo(Color(1, 1, 1)); + handle_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + handle_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + handle_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); + handle_material->set_on_top_of_alpha(); + if (p_billboard) { + handle_material->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); + handle_material->set_on_top_of_alpha(); + } + + materials[p_name] = Vector<Ref<SpatialMaterial> >(); + materials[p_name].push_back(handle_material); +} + +void EditorSpatialGizmoPlugin::add_material(const String &p_name, Ref<SpatialMaterial> p_material) { + materials[p_name] = Vector<Ref<SpatialMaterial> >(); + materials[p_name].push_back(p_material); +} + +Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name, EditorSpatialGizmo *p_gizmo) { + ERR_FAIL_COND_V(!materials.has(p_name), Ref<SpatialMaterial>()); + ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<SpatialMaterial>()); + + if (p_gizmo == NULL) return materials[p_name][0]; + + int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); + + Ref<SpatialMaterial> mat = materials[p_name][index]; + + if (current_state == ON_TOP && p_gizmo->is_selected()) { + mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true); + } else { + mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, false); + } + + return mat; +} + +Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) { + + Ref<EditorSpatialGizmo> ref = create_gizmo(p_spatial); + + if (ref.is_null()) return ref; + + ref->set_plugin(this); + ref->set_spatial_node(p_spatial); + ref->set_hidden(current_state == HIDDEN); + + current_gizmos.push_back(ref.ptr()); + return ref; +} + +bool EditorSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return false; +} + +Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::create_gizmo(Spatial *p_spatial) { + + Ref<EditorSpatialGizmo> ref; + if (has_gizmo(p_spatial)) ref.instance(); + return ref; +} + +bool EditorSpatialGizmoPlugin::can_be_hidden() const { + return true; +} + +bool EditorSpatialGizmoPlugin::is_selectable_when_hidden() const { + return false; +} + +void EditorSpatialGizmoPlugin::set_state(int p_state) { + current_state = p_state; + for (int i = 0; i < current_gizmos.size(); ++i) { + current_gizmos[i]->set_hidden(current_state == HIDDEN); + } +} + +void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) { + current_gizmos.erase(p_gizmo); +} + +EditorSpatialGizmoPlugin::EditorSpatialGizmoPlugin() { + current_state = ON_TOP; +} + +EditorSpatialGizmoPlugin::~EditorSpatialGizmoPlugin() { +} diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 637926a913..0ebc11e5df 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -43,11 +43,11 @@ class Camera; class SpatialEditor; -class SpatialEditorGizmos; +class EditorSpatialGizmoPlugin; -class SpatialEditorGizmo : public SpatialGizmo { +class EditorSpatialGizmo : public SpatialGizmo { - GDCLASS(SpatialEditorGizmo, SpatialGizmo); + GDCLASS(EditorSpatialGizmo, SpatialGizmo); bool selected; bool instanced; @@ -56,14 +56,86 @@ public: void set_selected(bool p_selected) { selected = p_selected; } bool is_selected() const { return selected; } + struct Instance { + + RID instance; + Ref<ArrayMesh> mesh; + RID skeleton; + bool billboard; + bool unscaled; + bool can_intersect; + bool extra_margin; + Instance() { + + billboard = false; + unscaled = false; + can_intersect = false; + extra_margin = false; + } + + void create_instance(Spatial *p_base, bool p_hidden = false); + }; + + Vector<Vector3> collision_segments; + Ref<TriangleMesh> collision_mesh; + + struct Handle { + Vector3 pos; + bool billboard; + }; + + Vector<Vector3> handles; + Vector<Vector3> secondary_handles; + float selectable_icon_size = -1.0f; + bool billboard_handle; + + bool valid; + bool hidden; + Spatial *base; + Vector<Instance> instances; + Spatial *spatial_node; + EditorSpatialGizmoPlugin *gizmo_plugin; + + void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to<Spatial>(p_node)); } + +protected: + static void _bind_methods(); + +public: + void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false); + void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID()); + void add_collision_segments(const Vector<Vector3> &p_lines); + void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); + void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1); + void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard = false, bool p_secondary = false); + void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3()); + virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; + virtual Variant get_handle_value(int p_idx); virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum); - virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); - SpatialEditorGizmo(); + void set_spatial_node(Spatial *p_node); + Spatial *get_spatial_node() const { return spatial_node; } + Vector3 get_handle_pos(int p_idx) const; + bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum); + bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); + + virtual void clear(); + virtual void create(); + virtual void transform(); + virtual void redraw(); + virtual void free(); + + //TODO remove (?) + virtual bool is_editable() const; + virtual bool can_draw() const; + + void set_hidden(bool p_hidden); + void set_plugin(EditorSpatialGizmoPlugin *p_gizmo); + + EditorSpatialGizmo(); + ~EditorSpatialGizmo(); }; class SpatialEditorViewport : public Control { @@ -93,7 +165,9 @@ class SpatialEditorViewport : public Control { VIEW_DISPLAY_NORMAL, VIEW_DISPLAY_WIREFRAME, VIEW_DISPLAY_OVERDRAW, - VIEW_DISPLAY_SHADELESS + VIEW_DISPLAY_SHADELESS, + VIEW_LOCK_ROTATION, + VIEW_CINEMATIC_PREVIEW }; public: @@ -107,7 +181,6 @@ private: int index; String name; void _menu_option(int p_option); - Spatial *preview_node; AABB *preview_bounds; Vector<String> selected_files; @@ -131,6 +204,7 @@ private: Camera *camera; bool transforming; bool orthogonal; + bool lock_rotation; float gizmo_scale; bool freelook_active; @@ -138,6 +212,7 @@ private: Label *info_label; Label *fps_label; + Label *cinema_label; struct _RayResult { @@ -229,7 +304,7 @@ private: int edited_gizmo; Point2 mouse_pos; bool snap; - Ref<SpatialEditorGizmo> gizmo; + Ref<EditorSpatialGizmo> gizmo; int gizmo_handle; Variant gizmo_initial_value; Vector3 gizmo_initial_pos; @@ -286,8 +361,11 @@ private: Camera *previewing; Camera *preview; + bool previewing_cinema; + void _preview_exited_scene(); void _toggle_camera_preview(bool); + void _toggle_cinema_preview(bool); void _init_gizmo_instance(int p_idx); void _finish_gizmo_instances(); void _selection_result_pressed(int); @@ -402,7 +480,6 @@ public: TOOL_LOCK_SELECTED, TOOL_UNLOCK_SELECTED, TOOL_MAX - }; enum ToolOptions { @@ -483,16 +560,18 @@ private: MENU_VIEW_USE_4_VIEWPORTS, MENU_VIEW_ORIGIN, MENU_VIEW_GRID, + MENU_VIEW_GIZMOS_3D_ICONS, MENU_VIEW_CAMERA_SETTINGS, MENU_LOCK_SELECTED, MENU_UNLOCK_SELECTED, - MENU_VISIBILITY_SKELETON + MENU_SNAP_TO_FLOOR }; Button *tool_button[TOOL_MAX]; Button *tool_option_button[TOOL_OPT_MAX]; MenuButton *transform_menu; + PopupMenu *gizmos_menu; MenuButton *view_menu; ToolButton *lock_button; @@ -524,6 +603,7 @@ private: void _xform_dialog_action(); void _menu_item_pressed(int p_option); void _menu_item_toggled(bool pressed, int p_option); + void _menu_gizmo_toggled(int p_option); HBoxContainer *hbc_menu; @@ -532,6 +612,7 @@ private: void _instance_scene(); void _init_indicators(); + void _init_gizmos_menu(); void _init_grid(); void _finish_indicators(); void _finish_grid(); @@ -551,7 +632,10 @@ private: static SpatialEditor *singleton; void _node_removed(Node *p_node); - SpatialEditorGizmos *gizmos; + Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins; + + void _register_all_gizmos(); + SpatialEditor(); bool is_any_freelook_active() const; @@ -591,11 +675,9 @@ public: Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } - int get_skeleton_visibility_state() const; - void update_transform_gizmo(); void update_all_gizmos(); - + void snap_selected_nodes_to_floor(); void select_gizmo_highlight_axis(int p_axis); void set_custom_camera(Node *p_camera) { custom_camera = p_camera; } @@ -625,6 +707,8 @@ public: return viewports[p_idx]; } + void register_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> ref); + Camera *get_camera() { return NULL; } void edit(Spatial *p_spatial); void clear(); @@ -661,4 +745,49 @@ public: ~SpatialEditorPlugin(); }; +class EditorSpatialGizmoPlugin : public Resource { + + GDCLASS(EditorSpatialGizmoPlugin, Resource); + +public: + static const int ON_TOP = 0; + static const int VISIBLE = 1; + static const int HIDDEN = 2; + +private: + int current_state; + List<EditorSpatialGizmo *> current_gizmos; + HashMap<String, Vector<Ref<SpatialMaterial> > > materials; + +protected: + virtual bool has_gizmo(Spatial *p_spatial); + virtual Ref<EditorSpatialGizmo> create_gizmo(Spatial *p_spatial); + +public: + void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); + void create_icon_material(const String &p_name, const Ref<Texture> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); + void create_handle_material(const String &p_name, bool p_billboard = false); + void add_material(const String &p_name, Ref<SpatialMaterial> p_material); + + Ref<SpatialMaterial> get_material(const String &p_name, EditorSpatialGizmo *p_gizmo = NULL); + + virtual String get_name() const = 0; + virtual bool can_be_hidden() const; + virtual bool is_selectable_when_hidden() const; + + virtual void redraw(EditorSpatialGizmo *p_gizmo) {} + virtual String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { return ""; } + virtual Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { return Variant(); } + virtual void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) {} + virtual void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) {} + virtual bool is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int idx) const { return false; } + + Ref<EditorSpatialGizmo> get_gizmo(Spatial *p_spatial); + void set_state(int p_state); + void unregister_gizmo(EditorSpatialGizmo *p_gizmo); + + EditorSpatialGizmoPlugin(); + virtual ~EditorSpatialGizmoPlugin(); +}; + #endif diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 66673cca00..9bf1178b58 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -169,7 +169,7 @@ void SpriteEditor::_update_mesh_data() { Size2 img_size = Vector2(image->get_width(), image->get_height()); for (int j = 0; j < lines.size(); j++) { - lines[j] = expand(lines[j], rect, epsilon); + lines.write[j] = expand(lines[j], rect, epsilon); int index_ofs = computed_vertices.size(); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp new file mode 100644 index 0000000000..16c25f3074 --- /dev/null +++ b/editor/plugins/text_editor.cpp @@ -0,0 +1,607 @@ +/*************************************************************************/ +/* text_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "text_editor.h" + +void TextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + highlighters[p_highlighter->get_name()] = p_highlighter; + highlighter_menu->add_radio_check_item(p_highlighter->get_name()); +} + +void TextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) { + TextEdit *te = code_editor->get_text_edit(); + te->_set_syntax_highlighting(p_highlighter); + if (p_highlighter != NULL) { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->get_name()), true); + } else { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text("Standard"), true); + } + + // little work around. GDScript highlighter goes through text_edit for colours, + // so to remove all colours we need to set and unset them here. + if (p_highlighter == NULL) { // standard + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->add_color_override("number_color", colors_cache.font_color); + text_edit->add_color_override("function_color", colors_cache.font_color); + text_edit->add_color_override("number_color", colors_cache.font_color); + text_edit->add_color_override("member_variable_color", colors_cache.font_color); + } else { + _load_theme_settings(); + } +} + +void TextEditor::_change_syntax_highlighter(int p_idx) { + Map<String, SyntaxHighlighter *>::Element *el = highlighters.front(); + while (el != NULL) { + highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false); + el = el->next(); + } + set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); +} + +void TextEditor::_load_theme_settings() { + + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->clear_colors(); + + Color background_color = EDITOR_GET("text_editor/highlighting/background_color"); + Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color"); + Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color"); + Color completion_existing_color = EDITOR_GET("text_editor/highlighting/completion_existing_color"); + Color completion_scroll_color = EDITOR_GET("text_editor/highlighting/completion_scroll_color"); + Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color"); + Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); + Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color"); + Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color"); + Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color"); + Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color"); + Color selection_color = EDITOR_GET("text_editor/highlighting/selection_color"); + Color brace_mismatch_color = EDITOR_GET("text_editor/highlighting/brace_mismatch_color"); + Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color"); + Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color"); + Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color"); + Color number_color = EDITOR_GET("text_editor/highlighting/number_color"); + Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); + Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); + Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); + Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); + Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); + Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); + Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); + Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color"); + Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color"); + Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + Color string_color = EDITOR_GET("text_editor/highlighting/string_color"); + + text_edit->add_color_override("background_color", background_color); + text_edit->add_color_override("completion_background_color", completion_background_color); + text_edit->add_color_override("completion_selected_color", completion_selected_color); + text_edit->add_color_override("completion_existing_color", completion_existing_color); + text_edit->add_color_override("completion_scroll_color", completion_scroll_color); + text_edit->add_color_override("completion_font_color", completion_font_color); + text_edit->add_color_override("font_color", text_color); + text_edit->add_color_override("line_number_color", line_number_color); + text_edit->add_color_override("caret_color", caret_color); + text_edit->add_color_override("caret_background_color", caret_background_color); + text_edit->add_color_override("font_selected_color", text_selected_color); + text_edit->add_color_override("selection_color", selection_color); + text_edit->add_color_override("brace_mismatch_color", brace_mismatch_color); + text_edit->add_color_override("current_line_color", current_line_color); + text_edit->add_color_override("line_length_guideline_color", line_length_guideline_color); + text_edit->add_color_override("word_highlighted_color", word_highlighted_color); + text_edit->add_color_override("number_color", number_color); + text_edit->add_color_override("function_color", function_color); + text_edit->add_color_override("member_variable_color", member_variable_color); + text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("mark_color", mark_color); + text_edit->add_color_override("code_folding_color", code_folding_color); + text_edit->add_color_override("search_result_color", search_result_color); + text_edit->add_color_override("search_result_border_color", search_result_border_color); + text_edit->add_color_override("symbol_color", symbol_color); + + text_edit->add_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 4)); + + colors_cache.font_color = text_color; + colors_cache.symbol_color = symbol_color; + colors_cache.keyword_color = keyword_color; + colors_cache.basetype_color = basetype_color; + colors_cache.type_color = type_color; + colors_cache.comment_color = comment_color; + colors_cache.string_color = string_color; +} + +String TextEditor::get_name() { + String name; + + if (text_file->get_path().find("local://") == -1 && text_file->get_path().find("::") == -1) { + name = text_file->get_path().get_file(); + if (is_unsaved()) { + name += "(*)"; + } + } else if (text_file->get_name() != "") { + name = text_file->get_name(); + } else { + name = text_file->get_class() + "(" + itos(text_file->get_instance_id()) + ")"; + } + + return name; +} + +Ref<Texture> TextEditor::get_icon() { + + if (get_parent_control() && get_parent_control()->has_icon(text_file->get_class(), "EditorIcons")) { + return get_parent_control()->get_icon(text_file->get_class(), "EditorIcons"); + } + return Ref<Texture>(); +} + +RES TextEditor::get_edited_resource() const { + return text_file; +} + +void TextEditor::set_edited_resource(const RES &p_res) { + ERR_FAIL_COND(!text_file.is_null()); + + text_file = p_res; + + code_editor->get_text_edit()->set_text(text_file->get_text()); + code_editor->get_text_edit()->clear_undo_history(); + code_editor->get_text_edit()->tag_saved_version(); + + emit_signal("name_changed"); + code_editor->update_line_and_column(); +} + +void TextEditor::add_callback(const String &p_function, PoolStringArray p_args) { +} + +void TextEditor::set_debugger_active(bool p_active) { +} + +void TextEditor::get_breakpoints(List<int> *p_breakpoints) { +} + +void TextEditor::reload_text() { + + ERR_FAIL_COND(text_file.is_null()); + + TextEdit *te = code_editor->get_text_edit(); + int column = te->cursor_get_column(); + int row = te->cursor_get_line(); + int h = te->get_h_scroll(); + int v = te->get_v_scroll(); + + te->set_text(text_file->get_text()); + te->clear_undo_history(); + te->cursor_set_line(row); + te->cursor_set_column(column); + te->set_h_scroll(h); + te->set_v_scroll(v); + + te->tag_saved_version(); + + code_editor->update_line_and_column(); +} + +void TextEditor::_validate_script() { + emit_signal("name_changed"); + emit_signal("edited_script_changed"); +} + +void TextEditor::apply_code() { + text_file->set_text(code_editor->get_text_edit()->get_text()); +} + +bool TextEditor::is_unsaved() { + + return code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version(); +} + +Variant TextEditor::get_edit_state() { + + return code_editor->get_edit_state(); +} + +void TextEditor::set_edit_state(const Variant &p_state) { + + code_editor->set_edit_state(p_state); +} + +void TextEditor::trim_trailing_whitespace() { + + code_editor->trim_trailing_whitespace(); +} + +void TextEditor::convert_indent_to_spaces() { + + code_editor->convert_indent_to_spaces(); +} + +void TextEditor::convert_indent_to_tabs() { + + code_editor->convert_indent_to_tabs(); +} + +void TextEditor::tag_saved_version() { + + code_editor->get_text_edit()->tag_saved_version(); +} + +void TextEditor::goto_line(int p_line, bool p_with_error) { + + code_editor->goto_line(p_line); +} + +void TextEditor::ensure_focus() { + + code_editor->get_text_edit()->grab_focus(); +} + +Vector<String> TextEditor::get_functions() { + + return Vector<String>(); +} + +bool TextEditor::show_members_overview() { + return true; +} + +void TextEditor::update_settings() { + + code_editor->update_editor_settings(); +} + +void TextEditor::set_tooltip_request_func(String p_method, Object *p_obj) { + + code_editor->get_text_edit()->set_tooltip_request_func(p_obj, p_method, this); +} + +Control *TextEditor::get_edit_menu() { + + return edit_hb; +} + +void TextEditor::clear_edit_menu() { + memdelete(edit_hb); +} + +void TextEditor::_notification(int p_what) { + + switch (p_what) { + case NOTIFICATION_READY: + _load_theme_settings(); + set_syntax_highlighter(NULL); + break; + } +} + +void TextEditor::_edit_option(int p_op) { + TextEdit *tx = code_editor->get_text_edit(); + + switch (p_op) { + case EDIT_UNDO: { + + tx->undo(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_REDO: { + + tx->redo(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_CUT: { + + tx->cut(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_COPY: { + + tx->copy(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_PASTE: { + + tx->paste(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_SELECT_ALL: { + + tx->select_all(); + tx->call_deferred("grab_focus"); + } break; + case EDIT_MOVE_LINE_UP: { + + code_editor->move_lines_up(); + } break; + case EDIT_MOVE_LINE_DOWN: { + + code_editor->move_lines_down(); + } break; + case EDIT_INDENT_LEFT: { + + tx->indent_left(); + } break; + case EDIT_INDENT_RIGHT: { + + tx->indent_right(); + } break; + case EDIT_DELETE_LINE: { + + code_editor->delete_lines(); + } break; + case EDIT_CLONE_DOWN: { + + code_editor->code_lines_down(); + } break; + case EDIT_TOGGLE_FOLD_LINE: { + + tx->toggle_fold_line(tx->cursor_get_line()); + tx->update(); + } break; + case EDIT_FOLD_ALL_LINES: { + + tx->fold_all_lines(); + tx->update(); + } break; + case EDIT_UNFOLD_ALL_LINES: { + + tx->unhide_all_lines(); + tx->update(); + } break; + case EDIT_TRIM_TRAILING_WHITESAPCE: { + + trim_trailing_whitespace(); + } break; + case EDIT_CONVERT_INDENT_TO_SPACES: { + + convert_indent_to_spaces(); + } break; + case EDIT_CONVERT_INDENT_TO_TABS: { + + convert_indent_to_tabs(); + } break; + case EDIT_TO_UPPERCASE: { + + _convert_case(CodeTextEditor::UPPER); + } break; + case EDIT_TO_LOWERCASE: { + + _convert_case(CodeTextEditor::LOWER); + } break; + case EDIT_CAPITALIZE: { + + _convert_case(CodeTextEditor::CAPITALIZE); + } break; + case SEARCH_FIND: { + + code_editor->get_find_replace_bar()->popup_search(); + } break; + case SEARCH_FIND_NEXT: { + + code_editor->get_find_replace_bar()->search_next(); + } break; + case SEARCH_FIND_PREV: { + + code_editor->get_find_replace_bar()->search_prev(); + } break; + case SEARCH_REPLACE: { + + code_editor->get_find_replace_bar()->popup_replace(); + } break; + case SEARCH_GOTO_LINE: { + + goto_line_dialog->popup_find_line(tx); + } break; + } +} + +void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { + + code_editor->convert_case(p_case); +} + +void TextEditor::_bind_methods() { + + ClassDB::bind_method("_validate_script", &TextEditor::_validate_script); + ClassDB::bind_method("_load_theme_settings", &TextEditor::_load_theme_settings); + ClassDB::bind_method("_edit_option", &TextEditor::_edit_option); + ClassDB::bind_method("_change_syntax_highlighter", &TextEditor::_change_syntax_highlighter); + ClassDB::bind_method("_text_edit_gui_input", &TextEditor::_text_edit_gui_input); +} + +static ScriptEditorBase *create_editor(const RES &p_resource) { + + if (Object::cast_to<TextFile>(*p_resource)) { + return memnew(TextEditor); + } + return NULL; +} + +void TextEditor::register_editor() { + + ScriptEditor::register_create_script_editor_function(create_editor); +} + +void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { + + Ref<InputEventMouseButton> mb = ev; + + if (mb.is_valid()) { + if (mb->get_button_index() == BUTTON_RIGHT) { + + int col, row; + TextEdit *tx = code_editor->get_text_edit(); + tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); + + tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); + bool can_fold = tx->can_fold(row); + bool is_folded = tx->is_folded(row); + + if (tx->is_right_click_moving_caret()) { + if (tx->is_selection_active()) { + + int from_line = tx->get_selection_from_line(); + int to_line = tx->get_selection_to_line(); + int from_column = tx->get_selection_from_column(); + int to_column = tx->get_selection_to_column(); + + if (row < from_line || row > to_line || (row == from_line && col < from_column) || (row == to_line && col > to_column)) { + // Right click is outside the seleted text + tx->deselect(); + } + } + if (!tx->is_selection_active()) { + tx->cursor_set_line(row, true, false); + tx->cursor_set_column(col); + } + } + + if (!mb->is_pressed()) { + _make_context_menu(tx->is_selection_active(), can_fold, is_folded); + } + } + } +} + +void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded) { + + context_menu->clear(); + if (p_selection) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + } + + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + + if (p_selection) { + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE); + } + if (p_can_fold || p_is_folded) + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); + + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); + context_menu->set_size(Vector2(1, 1)); + context_menu->popup(); +} + +TextEditor::TextEditor() { + code_editor = memnew(CodeTextEditor); + add_child(code_editor); + code_editor->add_constant_override("separation", 0); + code_editor->connect("load_theme_settings", this, "_load_theme_settings"); + code_editor->connect("validate_script", this, "_validate_script"); + code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); + code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + update_settings(); + + code_editor->get_text_edit()->set_context_menu_enabled(false); + code_editor->get_text_edit()->connect("gui_input", this, "_text_edit_gui_input"); + + context_menu = memnew(PopupMenu); + add_child(context_menu); + context_menu->connect("id_pressed", this, "_edit_option"); + + edit_hb = memnew(HBoxContainer); + + search_menu = memnew(MenuButton); + edit_hb->add_child(search_menu); + search_menu->set_text(TTR("Search")); + search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); + search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); + + goto_line_dialog = memnew(GotoLineDialog); + add_child(goto_line_dialog); + + edit_menu = memnew(MenuButton); + edit_menu->set_text(TTR("Edit")); + edit_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + + edit_hb->add_child(edit_menu); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); + + edit_menu->get_popup()->add_separator(); + PopupMenu *convert_case = memnew(PopupMenu); + convert_case->set_name("convert_case"); + edit_menu->get_popup()->add_child(convert_case); + edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case"); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE); + convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE); + convert_case->connect("id_pressed", this, "_edit_option"); + + highlighters["Standard"] = NULL; + highlighter_menu = memnew(PopupMenu); + highlighter_menu->set_name("highlighter_menu"); + edit_menu->get_popup()->add_child(highlighter_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu"); + highlighter_menu->add_radio_check_item(TTR("Standard")); + highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter"); + + code_editor->get_text_edit()->set_drag_forwarding(this); +} diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h new file mode 100644 index 0000000000..8b1983d891 --- /dev/null +++ b/editor/plugins/text_editor.h @@ -0,0 +1,146 @@ +/*************************************************************************/ +/* text_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXT_EDITOR_H +#define TEXT_EDITOR_H + +#include "script_editor_plugin.h" + +class TextEditor : public ScriptEditorBase { + + GDCLASS(TextEditor, ScriptEditorBase) + +private: + CodeTextEditor *code_editor; + + Ref<TextFile> text_file; + + HBoxContainer *edit_hb; + MenuButton *edit_menu; + PopupMenu *highlighter_menu; + MenuButton *search_menu; + PopupMenu *context_menu; + + GotoLineDialog *goto_line_dialog; + + struct ColorsCache { + Color font_color; + Color symbol_color; + Color keyword_color; + Color basetype_color; + Color type_color; + Color comment_color; + Color string_color; + } colors_cache; + + enum { + EDIT_UNDO, + EDIT_REDO, + EDIT_CUT, + EDIT_COPY, + EDIT_PASTE, + EDIT_SELECT_ALL, + EDIT_TRIM_TRAILING_WHITESAPCE, + EDIT_CONVERT_INDENT_TO_SPACES, + EDIT_CONVERT_INDENT_TO_TABS, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT_RIGHT, + EDIT_INDENT_LEFT, + EDIT_DELETE_LINE, + EDIT_CLONE_DOWN, + EDIT_TO_UPPERCASE, + EDIT_TO_LOWERCASE, + EDIT_CAPITALIZE, + EDIT_TOGGLE_FOLD_LINE, + EDIT_FOLD_ALL_LINES, + EDIT_UNFOLD_ALL_LINES, + SEARCH_FIND, + SEARCH_FIND_NEXT, + SEARCH_FIND_PREV, + SEARCH_REPLACE, + SEARCH_GOTO_LINE, + }; + +protected: + static void _bind_methods(); + + void _notification(int p_what); + + void _edit_option(int p_op); + void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded); + void _text_edit_gui_input(const Ref<InputEvent> &ev); + + Map<String, SyntaxHighlighter *> highlighters; + void _change_syntax_highlighter(int p_idx); + void _load_theme_settings(); + + void _convert_case(CodeTextEditor::CaseStyle p_case); + + void _validate_script(); + +public: + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); + virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); + + virtual String get_name(); + virtual Ref<Texture> get_icon(); + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); + void set_edited_file(const Ref<TextFile> &p_file); + virtual void reload_text(); + virtual void apply_code(); + virtual bool is_unsaved(); + virtual Variant get_edit_state(); + virtual void set_edit_state(const Variant &p_state); + virtual Vector<String> get_functions(); + virtual void get_breakpoints(List<int> *p_breakpoints); + virtual void goto_line(int p_line, bool p_with_error = false); + virtual void trim_trailing_whitespace(); + virtual void convert_indent_to_spaces(); + virtual void convert_indent_to_tabs(); + virtual void ensure_focus(); + virtual void tag_saved_version(); + virtual void update_settings(); + virtual bool show_members_overview(); + virtual bool can_lose_focus_on_node_selection() { return true; } + virtual void set_debugger_active(bool p_active); + virtual void set_tooltip_request_func(String p_method, Object *p_obj); + virtual void add_callback(const String &p_function, PoolStringArray p_args); + + virtual Control *get_edit_menu(); + virtual void clear_edit_menu(); + + static void register_editor(); + + TextEditor(); +}; + +#endif // TEXT_EDITOR_H diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index e4fdd1f251..0419c3d4b1 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -57,8 +57,6 @@ void TextureRegionEditor::_region_draw() { base_tex = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) base_tex = atlas_tex->get_atlas(); - else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) - base_tex = tile_set->tile_get_texture(selected_tile); if (base_tex.is_null()) return; @@ -284,8 +282,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { r = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) r = atlas_tex->get_region(); - else if (tile_set.is_valid() && selected_tile != -1) - r = tile_set->tile_get_region(selected_tile); rect.expand_to(r.position); rect.expand_to(r.position + r.size); } @@ -302,9 +298,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (atlas_tex.is_valid()) { undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect); undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region()); - } else if (tile_set.is_valid() && selected_tile != -1) { - undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, rect); - undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile)); } undo_redo->add_do_method(edit_draw, "update"); undo_redo->add_undo_method(edit_draw, "update"); @@ -327,8 +320,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { rect_prev = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) rect_prev = atlas_tex->get_region(); - else if (tile_set.is_valid() && selected_tile != -1) - rect_prev = tile_set->tile_get_region(selected_tile); for (int i = 0; i < 8; i++) { Vector2 tuv = endpoints[i]; @@ -372,9 +363,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } else if (obj_styleBox.is_valid()) { undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect()); undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", rect_prev); - } else if (tile_set.is_valid()) { - undo_redo->add_do_method(tile_set.ptr(), "tile_set_region", selected_tile, tile_set->tile_get_region(selected_tile)); - undo_redo->add_undo_method(tile_set.ptr(), "tile_set_region", selected_tile, rect_prev); } drag_index = -1; } @@ -595,8 +583,6 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) { obj_styleBox->set_region_rect(rect); else if (atlas_tex.is_valid()) atlas_tex->set_region(rect); - else if (tile_set.is_valid() && selected_tile != -1) - tile_set->tile_set_region(selected_tile, rect); } void TextureRegionEditor::_notification(int p_what) { @@ -623,12 +609,11 @@ void TextureRegionEditor::_notification(int p_what) { } void TextureRegionEditor::_node_removed(Object *p_obj) { - if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr() || p_obj == tile_set.ptr()) { + if (p_obj == node_sprite || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) { node_ninepatch = NULL; node_sprite = NULL; obj_styleBox = Ref<StyleBox>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); - tile_set = Ref<TileSet>(NULL); hide(); } } @@ -677,8 +662,6 @@ void TextureRegionEditor::edit(Object *p_obj) { obj_styleBox->remove_change_receptor(this); if (atlas_tex.is_valid()) atlas_tex->remove_change_receptor(this); - if (tile_set.is_valid()) - tile_set->remove_change_receptor(this); if (p_obj) { node_sprite = Object::cast_to<Sprite>(p_obj); node_ninepatch = Object::cast_to<NinePatchRect>(p_obj); @@ -686,8 +669,6 @@ void TextureRegionEditor::edit(Object *p_obj) { obj_styleBox = Ref<StyleBoxTexture>(Object::cast_to<StyleBoxTexture>(p_obj)); if (Object::cast_to<AtlasTexture>(p_obj)) atlas_tex = Ref<AtlasTexture>(Object::cast_to<AtlasTexture>(p_obj)); - if (Object::cast_to<TileSet>(p_obj)) - tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_obj)); p_obj->add_change_receptor(this); _edit_region(); } else { @@ -695,7 +676,6 @@ void TextureRegionEditor::edit(Object *p_obj) { node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); - tile_set = Ref<TileSet>(NULL); } edit_draw->update(); if (node_sprite && !node_sprite->is_region()) { @@ -724,8 +704,6 @@ void TextureRegionEditor::_edit_region() { texture = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) texture = atlas_tex->get_atlas(); - else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) - texture = tile_set->tile_get_texture(selected_tile); if (texture.is_null()) { edit_draw->update(); @@ -794,8 +772,6 @@ void TextureRegionEditor::_edit_region() { rect = obj_styleBox->get_region_rect(); else if (atlas_tex.is_valid()) rect = atlas_tex->get_region(); - else if (tile_set.is_valid() && selected_tile != -1) - rect = tile_set->tile_get_region(selected_tile); edit_draw->update(); } @@ -814,10 +790,8 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { node_ninepatch = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); atlas_tex = Ref<AtlasTexture>(NULL); - tile_set = Ref<TileSet>(NULL); editor = p_editor; undo_redo = editor->get_undo_redo(); - selected_tile = -1; snap_step = Vector2(10, 10); snap_separation = Vector2(0, 0); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index eeba1987a6..bd93be9267 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -38,7 +38,6 @@ #include "scene/gui/nine_patch_rect.h" #include "scene/resources/style_box.h" #include "scene/resources/texture.h" -#include "scene/resources/tile_set.h" /** @author Mariano Suligoy @@ -56,8 +55,6 @@ class TextureRegionEditor : public Control { }; friend class TextureRegionEditorPlugin; - friend class TileSetEditor; - friend class TileSetEditorPlugin; MenuButton *snap_mode_button; TextureRect *icon_zoom; ToolButton *zoom_in; @@ -91,14 +88,12 @@ class TextureRegionEditor : public Control { Sprite *node_sprite; Ref<StyleBoxTexture> obj_styleBox; Ref<AtlasTexture> atlas_tex; - Ref<TileSet> tile_set; Rect2 rect; Rect2 rect_prev; float prev_margin; int edited_margin; List<Rect2> autoslice_cache; - int selected_tile; bool drag; bool creating; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 19646f37b5..101dc3037f 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -73,7 +73,8 @@ void TileMapEditor::_notification(int p_what) { rotate_180->set_icon(get_icon("Rotate180", "EditorIcons")); rotate_270->set_icon(get_icon("Rotate270", "EditorIcons")); - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); PopupMenu *p = options->get_popup(); p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); @@ -168,10 +169,11 @@ void TileMapEditor::_menu_option(int p_option) { } void TileMapEditor::_palette_selected(int index) { + _update_palette(); +} - if (manual_autotile) { - _update_palette(); - } +void TileMapEditor::_palette_multi_selected(int index, bool selected) { + _update_palette(); } void TileMapEditor::_canvas_mouse_enter() { @@ -196,7 +198,7 @@ Vector<int> TileMapEditor::get_selected_tiles() const { } for (int i = items.size() - 1; i >= 0; i--) { - items[i] = palette->get_item_metadata(items[i]); + items.write[i] = palette->get_item_metadata(items[i]); } return items; } @@ -296,7 +298,7 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose); - if (manual_autotile) { + if (manual_autotile || node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); } @@ -317,7 +319,6 @@ void TileMapEditor::_text_entered(const String &p_text) { } void TileMapEditor::_text_changed(const String &p_text) { - _update_palette(); } @@ -427,7 +428,7 @@ void TileMapEditor::_update_palette() { if (tex.is_valid()) { Rect2 region = tileset->tile_get_region(entries[i].id); - if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE) { + if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) { int spacing = tileset->autotile_get_spacing(entries[i].id); region.size = tileset->autotile_get_size(entries[i].id); region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id); @@ -450,7 +451,7 @@ void TileMapEditor::_update_palette() { palette->select(0); } - if (manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) { + if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(sel_tile); @@ -526,7 +527,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era if (!erase) { ids = get_selected_tiles(); - if (ids.size() == 0 && ids[0] == TileMap::INVALID_CELL) + if (ids.size() == 0 || ids[0] == TileMap::INVALID_CELL) return PoolVector<Vector2>(); } else if (prev_id == TileMap::INVALID_CELL) { return PoolVector<Vector2>(); @@ -538,9 +539,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era } } - Rect2i r = node->_edit_get_rect(); - r.position = r.position / node->get_cell_size(); - r.size = r.size / node->get_cell_size(); + Rect2i r = node->get_used_rect(); int area = r.get_area(); if (preview) { @@ -678,10 +677,10 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell); Rect2 r = node->get_tileset()->tile_get_region(p_cell); - if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE) { + if (node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::AUTO_TILE || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) { Vector2 offset; int selected = manual_palette->get_current(); - if (manual_autotile && selected != -1) { + if ((manual_autotile || node->get_tileset()->tile_get_tile_mode(p_cell) == TileSet::ATLAS_TILE) && selected != -1) { offset = manual_palette->get_item_metadata(selected); } else { offset = node->get_tileset()->autotile_get_icon_coordinate(p_cell); @@ -985,7 +984,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { ids.push_back(0); for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) { - ids[0] = E->get().cell; + ids.write[0] = E->get().cell; _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose); } _finish_undo(); @@ -1008,7 +1007,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) { - ids[0] = E->get().cell; + ids.write[0] = E->get().cell; _set_cell(E->get().pos + ofs, ids, E->get().flip_h, E->get().flip_v, E->get().transpose); } _finish_undo(); @@ -1029,7 +1028,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (points.size() == 0) return false; - undo_redo->create_action(TTR("Bucket Fill")); + _start_undo(TTR("Bucket Fill")); Dictionary op; op["id"] = get_selected_tiles(); @@ -1039,7 +1038,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _fill_points(points, op); - undo_redo->commit_action(); + _finish_undo(); // We want to keep the bucket-tool active return true; @@ -1230,7 +1229,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) { - tmp_cell[0] = E->get().idx; + tmp_cell.write[0] = E->get().idx; _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr); } } @@ -1267,7 +1266,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) { - tmp_cell[0] = E->get().idx; + tmp_cell.write[0] = E->get().idx; _set_cell(E->key(), tmp_cell, E->get().xf, E->get().yf, E->get().tr); } } @@ -1675,6 +1674,7 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_tileset_settings_changed"), &TileMapEditor::_tileset_settings_changed); ClassDB::bind_method(D_METHOD("_update_transform_buttons"), &TileMapEditor::_update_transform_buttons); 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("_fill_points"), &TileMapEditor::_fill_points); ClassDB::bind_method(D_METHOD("_erase_points"), &TileMapEditor::_erase_points); @@ -1752,7 +1752,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { bucket_cache_visited = 0; invalid_cell.resize(1); - invalid_cell[0] = TileMap::INVALID_CELL; + invalid_cell.write[0] = TileMap::INVALID_CELL; ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE); ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F); @@ -1802,6 +1802,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { palette->set_max_text_lines(2); palette->set_select_mode(ItemList::SELECT_MULTI); palette->connect("item_selected", this, "_palette_selected"); + palette->connect("multi_selected", this, "_palette_multi_selected"); palette_container->add_child(palette); // Add autotile override palette diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index b8443ca962..bb76879b02 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -184,6 +184,7 @@ class TileMapEditor : public VBoxContainer { void _update_palette(); void _menu_option(int p_option); void _palette_selected(int index); + void _palette_multi_selected(int index, bool selected); void _start_undo(const String &p_action); void _finish_undo(); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 087c4293f1..8d1db5de8f 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -30,6 +30,8 @@ #include "tile_set_editor_plugin.h" +#include "core/os/input.h" +#include "core/os/keyboard.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/sprite.h" @@ -39,7 +41,9 @@ void TileSetEditor::edit(const Ref<TileSet> &p_tileset) { tileset = p_tileset; tileset->add_change_receptor(this); - update_tile_list(); + texture_list->clear(); + texture_map.clear(); + update_texture_list(); } void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) { @@ -161,75 +165,6 @@ void TileSetEditor::_import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_ _import_node(p_scene, p_library); } -void TileSetEditor::_menu_confirm() { - - switch (option) { - - case MENU_OPTION_MERGE_FROM_SCENE: - case MENU_OPTION_CREATE_FROM_SCENE: { - - EditorNode *en = editor; - Node *scene = en->get_edited_scene(); - if (!scene) - break; - - _import_scene(scene, tileset, option == MENU_OPTION_MERGE_FROM_SCENE); - - } break; - } -} - -void TileSetEditor::_name_dialog_confirm(const String &name) { - - switch (option) { - - case MENU_OPTION_REMOVE_ITEM: { - - int id = tileset->find_tile_by_name(name); - - if (id < 0 && name.is_valid_integer()) - id = name.to_int(); - - if (tileset->has_tile(id)) { - tileset->remove_tile(id); - update_tile_list(); - } else { - err_dialog->set_text(TTR("Could not find tile:") + " " + name); - err_dialog->popup_centered(Size2(300, 60)); - } - } break; - } -} - -void TileSetEditor::_menu_cbk(int p_option) { - - option = p_option; - switch (p_option) { - - case MENU_OPTION_ADD_ITEM: { - tileset->create_tile(tileset->get_last_unused_tile_id()); - tileset->tile_set_name(tileset->get_last_unused_tile_id() - 1, itos(tileset->get_last_unused_tile_id() - 1)); - update_tile_list(); - } break; - case MENU_OPTION_REMOVE_ITEM: { - - nd->set_title(TTR("Remove Item")); - nd->set_text(TTR("Item name or ID:")); - nd->popup_centered(Size2(300, 95)); - } break; - case MENU_OPTION_CREATE_FROM_SCENE: { - - cd->set_text(TTR("Create from scene?")); - cd->popup_centered(Size2(300, 60)); - } break; - case MENU_OPTION_MERGE_FROM_SCENE: { - - cd->set_text(TTR("Merge from scene?")); - cd->popup_centered(Size2(300, 60)); - } break; - } -} - Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge) { _import_scene(p_base_scene, ml, p_merge); @@ -237,28 +172,36 @@ Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bo } void TileSetEditor::_bind_methods() { - - ClassDB::bind_method("_menu_cbk", &TileSetEditor::_menu_cbk); - ClassDB::bind_method("_menu_confirm", &TileSetEditor::_menu_confirm); - ClassDB::bind_method("_name_dialog_confirm", &TileSetEditor::_name_dialog_confirm); - ClassDB::bind_method("_on_tile_list_selected", &TileSetEditor::_on_tile_list_selected); + ClassDB::bind_method("_on_tileset_toolbar_button_pressed", &TileSetEditor::_on_tileset_toolbar_button_pressed); + ClassDB::bind_method("_on_textures_added", &TileSetEditor::_on_textures_added); + 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_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); ClassDB::bind_method("_on_workspace_draw", &TileSetEditor::_on_workspace_draw); ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input); ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked); ClassDB::bind_method("_on_priority_changed", &TileSetEditor::_on_priority_changed); ClassDB::bind_method("_on_grid_snap_toggled", &TileSetEditor::_on_grid_snap_toggled); - ClassDB::bind_method("_set_snap_step_x", &TileSetEditor::_set_snap_step_x); - ClassDB::bind_method("_set_snap_step_y", &TileSetEditor::_set_snap_step_y); - ClassDB::bind_method("_set_snap_off_x", &TileSetEditor::_set_snap_off_x); - ClassDB::bind_method("_set_snap_off_y", &TileSetEditor::_set_snap_off_y); - ClassDB::bind_method("_set_snap_sep_x", &TileSetEditor::_set_snap_sep_x); - ClassDB::bind_method("_set_snap_sep_y", &TileSetEditor::_set_snap_sep_y); + ClassDB::bind_method("_set_snap_step", &TileSetEditor::_set_snap_step); + ClassDB::bind_method("_set_snap_off", &TileSetEditor::_set_snap_off); + ClassDB::bind_method("_set_snap_sep", &TileSetEditor::_set_snap_sep); } void TileSetEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + + tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_icon(get_icon("ToolAddNode", "EditorIcons")); + tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_icon(get_icon("Remove", "EditorIcons")); + tileset_toolbar_tools->set_icon(get_icon("Tools", "EditorIcons")); + + tool_workspacemode[WORKSPACE_EDIT]->set_icon(get_icon("Edit", "EditorIcons")); + tool_workspacemode[WORKSPACE_CREATE_SINGLE]->set_icon(get_icon("AddSingleTile", "EditorIcons")); + tool_workspacemode[WORKSPACE_CREATE_AUTOTILE]->set_icon(get_icon("AddAutotile", "EditorIcons")); + tool_workspacemode[WORKSPACE_CREATE_ATLAS]->set_icon(get_icon("AddAtlasTile", "EditorIcons")); + tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons")); tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons")); @@ -266,91 +209,126 @@ void TileSetEditor::_notification(int p_what) { tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons")); tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); - tools[SHAPE_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); + tools[TOOL_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons")); tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); + tools[VISIBLE_INFO]->set_icon(get_icon("InformationSign", "EditorIcons")); + + tool_editmode[EDITMODE_REGION]->set_icon(get_icon("RegionEdit", "EditorIcons")); + tool_editmode[EDITMODE_COLLISION]->set_icon(get_icon("StaticBody2D", "EditorIcons")); + tool_editmode[EDITMODE_OCCLUSION]->set_icon(get_icon("LightOccluder2D", "EditorIcons")); + tool_editmode[EDITMODE_NAVIGATION]->set_icon(get_icon("Navigation2D", "EditorIcons")); + tool_editmode[EDITMODE_BITMASK]->set_icon(get_icon("PackedDataContainer", "EditorIcons")); + tool_editmode[EDITMODE_PRIORITY]->set_icon(get_icon("MaterialPreviewLight1", "EditorIcons")); + tool_editmode[EDITMODE_ICON]->set_icon(get_icon("LargeTexture", "EditorIcons")); } } -void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) { - if (p_prop == StringName("region")) { - update_tile_list_icon(); - preview->set_region_rect(tileset->tile_get_region(get_current_tile())); - } else if (p_prop == StringName("name")) { - update_tile_list_icon(); - } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) { - _on_tile_list_selected(get_current_tile()); - workspace->update(); - preview->set_texture(tileset->tile_get_texture(get_current_tile())); - preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); - preview->set_region_rect(tileset->tile_get_region(get_current_tile())); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) - property_editor->show(); - else - property_editor->hide(); - texture_region_editor->_edit_region(); - update_tile_list_icon(); - } else if (p_prop == StringName("autotile")) { - workspace->update(); - } -} +TileSetEditor::TileSetEditor(EditorNode *p_editor) { -void TileSetEditor::initialize_bottom_editor() { + editor = p_editor; + set_name("Tile Set Bottom Editor"); - //Side Panel - side_panel = memnew(Control); - side_panel->set_name("Tile Set"); + HSplitContainer *split = memnew(HSplitContainer); + split->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_MINSIZE, 10); + add_child(split); - VSplitContainer *split = memnew(VSplitContainer); - side_panel->add_child(split); - split->set_anchors_and_margins_preset(Control::PRESET_WIDE); + VBoxContainer *left_container = memnew(VBoxContainer); + split->add_child(left_container); - tile_list = memnew(ItemList); - tile_list->set_v_size_flags(SIZE_EXPAND_FILL); - tile_list->set_h_size_flags(SIZE_EXPAND_FILL); - tile_list->set_custom_minimum_size(Size2(10, 200)); - tile_list->connect("item_selected", this, "_on_tile_list_selected"); - split->add_child(tile_list); + texture_list = memnew(ItemList); + left_container->add_child(texture_list); + texture_list->set_v_size_flags(SIZE_EXPAND_FILL); + texture_list->set_custom_minimum_size(Size2(200, 0)); + texture_list->connect("item_selected", this, "_on_texture_list_selected"); - property_editor = memnew(PropertyEditor); - property_editor->set_v_size_flags(SIZE_EXPAND_FILL); - property_editor->set_h_size_flags(SIZE_EXPAND_FILL); - property_editor->set_custom_minimum_size(Size2(10, 70)); - split->add_child(property_editor); + HBoxContainer *tileset_toolbar_container = memnew(HBoxContainer); + left_container->add_child(tileset_toolbar_container); - helper = memnew(TileSetEditorHelper(this)); - property_editor->call_deferred("edit", helper); - helper->add_change_receptor(this); + tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE] = memnew(ToolButton); + Vector<Variant> p; + p.push_back((int)TOOL_TILESET_ADD_TEXTURE); + tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->connect("pressed", this, "_on_tileset_toolbar_button_pressed", p); + tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]); + tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_tooltip(TTR("Add Texture(s) to TileSet")); + + tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE] = memnew(ToolButton); + p = Vector<Variant>(); + p.push_back((int)TOOL_TILESET_REMOVE_TEXTURE); + tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->connect("pressed", this, "_on_tileset_toolbar_button_pressed", p); + tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]); + tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_tooltip(TTR("Remove current Texture from TileSet")); + + Control *toolbar_separator = memnew(Control); + toolbar_separator->set_h_size_flags(Control::SIZE_EXPAND_FILL); + tileset_toolbar_container->add_child(toolbar_separator); + + tileset_toolbar_tools = memnew(MenuButton); + tileset_toolbar_tools->set_text("Tools"); + p = Vector<Variant>(); + p.push_back((int)TOOL_TILESET_CREATE_SCENE); + tileset_toolbar_tools->get_popup()->add_item(TTR("Create from Scene"), TOOL_TILESET_CREATE_SCENE); + p = Vector<Variant>(); + p.push_back((int)TOOL_TILESET_MERGE_SCENE); + tileset_toolbar_tools->get_popup()->add_item(TTR("Merge from Scene"), TOOL_TILESET_MERGE_SCENE); - //Editor - //Bottom Panel - bottom_panel = memnew(Control); - bottom_panel->set_name("Tile Set Bottom Editor"); + tileset_toolbar_tools->get_popup()->connect("id_pressed", this, "_on_tileset_toolbar_button_pressed"); + tileset_toolbar_container->add_child(tileset_toolbar_tools); + + //--------------- + VBoxContainer *right_container = memnew(VBoxContainer); + right_container->set_v_size_flags(SIZE_EXPAND_FILL); + split->add_child(right_container); dragging_point = -1; creating_shape = false; snap_step = Vector2(32, 32); + snap_offset = WORKSPACE_MARGIN; - bottom_panel->set_custom_minimum_size(Size2(0, 150)); + set_custom_minimum_size(Size2(0, 150)); VBoxContainer *main_vb = memnew(VBoxContainer); - bottom_panel->add_child(main_vb); - main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE); + right_container->add_child(main_vb); + main_vb->set_v_size_flags(SIZE_EXPAND_FILL); HBoxContainer *tool_hb = memnew(HBoxContainer); Ref<ButtonGroup> g(memnew(ButtonGroup)); - String label[EDITMODE_MAX] = { "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" }; + String workspace_label[WORKSPACE_MODE_MAX] = { "Edit", "New Single Tile", "New Autotile", "New Atlas" }; + + for (int i = 0; i < (int)WORKSPACE_MODE_MAX; i++) { + tool_workspacemode[i] = memnew(Button); + tool_workspacemode[i]->set_text(workspace_label[i]); + tool_workspacemode[i]->set_toggle_mode(true); + tool_workspacemode[i]->set_button_group(g); + Vector<Variant> p; + p.push_back(i); + tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", p); + tool_hb->add_child(tool_workspacemode[i]); + } + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); + workspace_mode = WORKSPACE_EDIT; + + main_vb->add_child(tool_hb); + main_vb->add_child(memnew(HSeparator)); + + tool_hb = memnew(HBoxContainer); + Control *spacer = memnew(Control); + spacer->set_custom_minimum_size(Size2(30, 0)); + tool_hb->add_child(spacer); + + g = Ref<ButtonGroup>(memnew(ButtonGroup)); + String label[EDITMODE_MAX] = { "Region", "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" }; for (int i = 0; i < (int)EDITMODE_MAX; i++) { tool_editmode[i] = memnew(Button); tool_editmode[i]->set_text(label[i]); tool_editmode[i]->set_toggle_mode(true); tool_editmode[i]->set_button_group(g); - Vector<Variant> args; - args.push_back(i); - tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", args); + Vector<Variant> p; + p.push_back(i); + tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", p); tool_hb->add_child(tool_editmode[i]); } tool_editmode[EDITMODE_COLLISION]->set_pressed(true); @@ -360,127 +338,52 @@ void TileSetEditor::initialize_bottom_editor() { main_vb->add_child(memnew(HSeparator)); toolbar = memnew(HBoxContainer); - for (int i = 0; i < (int)TOOLBAR_MAX; i++) { - tool_containers[i] = memnew(HBoxContainer); - toolbar->add_child(tool_containers[i]); - tool_containers[i]->hide(); - } - Ref<ButtonGroup> tg(memnew(ButtonGroup)); - Vector<Variant> p; + p = Vector<Variant>(); tools[TOOL_SELECT] = memnew(ToolButton); - tool_containers[TOOLBAR_DUMMY]->add_child(tools[TOOL_SELECT]); + toolbar->add_child(tools[TOOL_SELECT]); tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.")); tools[TOOL_SELECT]->set_toggle_mode(true); tools[TOOL_SELECT]->set_button_group(tg); tools[TOOL_SELECT]->set_pressed(true); p.push_back((int)TOOL_SELECT); tools[TOOL_SELECT]->connect("pressed", this, "_on_tool_clicked", p); - tool_containers[TOOLBAR_DUMMY]->show(); tools[BITMASK_COPY] = memnew(ToolButton); p.push_back((int)BITMASK_COPY); tools[BITMASK_COPY]->connect("pressed", this, "_on_tool_clicked", p); - tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_COPY]); + toolbar->add_child(tools[BITMASK_COPY]); tools[BITMASK_PASTE] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)BITMASK_PASTE); tools[BITMASK_PASTE]->connect("pressed", this, "_on_tool_clicked", p); - tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_PASTE]); + toolbar->add_child(tools[BITMASK_PASTE]); tools[BITMASK_CLEAR] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)BITMASK_CLEAR); tools[BITMASK_CLEAR]->connect("pressed", this, "_on_tool_clicked", p); - tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_CLEAR]); + toolbar->add_child(tools[BITMASK_CLEAR]); tools[SHAPE_NEW_POLYGON] = memnew(ToolButton); - tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_NEW_POLYGON]); + toolbar->add_child(tools[SHAPE_NEW_POLYGON]); tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true); tools[SHAPE_NEW_POLYGON]->set_button_group(tg); - tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator)); + toolbar->add_child(memnew(VSeparator)); tools[SHAPE_DELETE] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)SHAPE_DELETE); tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p); - tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_DELETE]); - tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator)); + toolbar->add_child(tools[SHAPE_DELETE]); + toolbar->add_child(memnew(VSeparator)); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton); tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); - tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_KEEP_INSIDE_TILE]); - tools[SHAPE_GRID_SNAP] = memnew(ToolButton); - tools[SHAPE_GRID_SNAP]->set_toggle_mode(true); - tools[SHAPE_GRID_SNAP]->connect("toggled", this, "_on_grid_snap_toggled"); - tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_GRID_SNAP]); - - hb_grid = memnew(HBoxContainer); - tool_containers[TOOLBAR_SHAPE]->add_child(hb_grid); - - hb_grid->add_child(memnew(VSeparator)); - hb_grid->add_child(memnew(Label(TTR("Offset:")))); - - sb_off_x = memnew(SpinBox); - sb_off_x->set_min(-256); - sb_off_x->set_max(256); - sb_off_x->set_step(1); - sb_off_x->set_value(snap_offset.x); - sb_off_x->set_suffix("px"); - sb_off_x->connect("value_changed", this, "_set_snap_off_x"); - hb_grid->add_child(sb_off_x); - - sb_off_y = memnew(SpinBox); - sb_off_y->set_min(-256); - sb_off_y->set_max(256); - sb_off_y->set_step(1); - sb_off_y->set_value(snap_offset.y); - sb_off_y->set_suffix("px"); - sb_off_y->connect("value_changed", this, "_set_snap_off_y"); - hb_grid->add_child(sb_off_y); - - hb_grid->add_child(memnew(VSeparator)); - hb_grid->add_child(memnew(Label(TTR("Step:")))); - - sb_step_x = memnew(SpinBox); - sb_step_x->set_min(-256); - sb_step_x->set_max(256); - sb_step_x->set_step(1); - sb_step_x->set_value(snap_step.x); - sb_step_x->set_suffix("px"); - sb_step_x->connect("value_changed", this, "_set_snap_step_x"); - hb_grid->add_child(sb_step_x); - - sb_step_y = memnew(SpinBox); - sb_step_y->set_min(-256); - sb_step_y->set_max(256); - sb_step_y->set_step(1); - sb_step_y->set_value(snap_step.y); - sb_step_y->set_suffix("px"); - sb_step_y->connect("value_changed", this, "_set_snap_step_y"); - hb_grid->add_child(sb_step_y); - - hb_grid->add_child(memnew(VSeparator)); - hb_grid->add_child(memnew(Label(TTR("Separation:")))); - - sb_sep_x = memnew(SpinBox); - sb_sep_x->set_min(0); - sb_sep_x->set_max(256); - sb_sep_x->set_step(1); - sb_sep_x->set_value(snap_separation.x); - sb_sep_x->set_suffix("px"); - sb_sep_x->connect("value_changed", this, "_set_snap_sep_x"); - hb_grid->add_child(sb_sep_x); - - sb_sep_y = memnew(SpinBox); - sb_sep_y->set_min(0); - sb_sep_y->set_max(256); - sb_sep_y->set_step(1); - sb_sep_y->set_value(snap_separation.y); - sb_sep_y->set_suffix("px"); - sb_sep_y->connect("value_changed", this, "_set_snap_sep_y"); - hb_grid->add_child(sb_sep_y); - - hb_grid->hide(); + toolbar->add_child(tools[SHAPE_KEEP_INSIDE_TILE]); + tools[TOOL_GRID_SNAP] = memnew(ToolButton); + tools[TOOL_GRID_SNAP]->set_toggle_mode(true); + tools[TOOL_GRID_SNAP]->connect("toggled", this, "_on_grid_snap_toggled"); + toolbar->add_child(tools[TOOL_GRID_SNAP]); spin_priority = memnew(SpinBox); spin_priority->set_min(1); @@ -491,8 +394,6 @@ void TileSetEditor::initialize_bottom_editor() { spin_priority->hide(); toolbar->add_child(spin_priority); - tool_containers[TOOLBAR_SHAPE]->show(); - Control *separator = memnew(Control); separator->set_h_size_flags(SIZE_EXPAND_FILL); toolbar->add_child(separator); @@ -502,22 +403,31 @@ void TileSetEditor::initialize_bottom_editor() { p.push_back((int)ZOOM_OUT); tools[ZOOM_OUT]->connect("pressed", this, "_on_tool_clicked", p); toolbar->add_child(tools[ZOOM_OUT]); + tools[ZOOM_OUT]->set_tooltip(TTR("Zoom Out")); tools[ZOOM_1] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)ZOOM_1); tools[ZOOM_1]->connect("pressed", this, "_on_tool_clicked", p); toolbar->add_child(tools[ZOOM_1]); + tools[ZOOM_1]->set_tooltip(TTR("Reset Zoom")); tools[ZOOM_IN] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)ZOOM_IN); tools[ZOOM_IN]->connect("pressed", this, "_on_tool_clicked", p); toolbar->add_child(tools[ZOOM_IN]); + tools[ZOOM_IN]->set_tooltip(TTR("Zoom In")); + + tools[VISIBLE_INFO] = memnew(ToolButton); + tools[VISIBLE_INFO]->set_toggle_mode(true); + tools[VISIBLE_INFO]->set_tooltip(TTR("Display tile's names (hold Alt Key)")); + toolbar->add_child(tools[VISIBLE_INFO]); main_vb->add_child(toolbar); scroll = memnew(ScrollContainer); main_vb->add_child(scroll); scroll->set_v_size_flags(SIZE_EXPAND_FILL); + scroll->set_clip_contents(true); workspace_container = memnew(Control); scroll->add_child(workspace_container); @@ -527,6 +437,7 @@ void TileSetEditor::initialize_bottom_editor() { workspace_container->add_child(workspace_overlay); workspace = memnew(Control); + workspace->set_focus_mode(FOCUS_ALL); workspace->connect("draw", this, "_on_workspace_draw"); workspace->connect("gui_input", this, "_on_workspace_input"); workspace->set_draw_behind_parent(true); @@ -536,39 +447,35 @@ void TileSetEditor::initialize_bottom_editor() { workspace->add_child(preview); preview->set_centered(false); preview->set_draw_behind_parent(true); - preview->set_region(true); -} + preview->set_position(WORKSPACE_MARGIN); -TileSetEditor::TileSetEditor(EditorNode *p_editor) { - - menu = memnew(MenuButton); - CanvasItemEditor::get_singleton()->add_control_to_menu_panel(menu); - menu->hide(); - menu->set_text(TTR("Tile Set")); - menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); - menu->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM); - menu->get_popup()->add_separator(); - menu->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE); - menu->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE); - menu->get_popup()->connect("id_pressed", this, "_menu_cbk"); - editor = p_editor; + //--------------- cd = memnew(ConfirmationDialog); add_child(cd); - cd->get_ok()->connect("pressed", this, "_menu_confirm"); - - nd = memnew(EditorNameDialog); - add_child(nd); - nd->set_hide_on_ok(true); - nd->get_line_edit()->set_margin(MARGIN_TOP, 28); - nd->connect("name_confirmed", this, "_name_dialog_confirm"); + cd->connect("confirmed", this, "_on_tileset_toolbar_confirm"); + //--------------- err_dialog = memnew(AcceptDialog); add_child(err_dialog); - err_dialog->set_title(TTR("Error")); - draw_handles = false; + //--------------- + texture_dialog = memnew(EditorFileDialog); + texture_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES); + texture_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILES); + texture_dialog->clear_filters(); + List<String> extensions; + + ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions); + for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - initialize_bottom_editor(); + texture_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper()); + } + add_child(texture_dialog); + texture_dialog->connect("files_selected", this, "_on_textures_added"); + + //--------------- + helper = memnew(TilesetEditorContext(this)); + tile_names_opacity = 0; } TileSetEditor::~TileSetEditor() { @@ -576,57 +483,166 @@ TileSetEditor::~TileSetEditor() { memdelete(helper); } -void TileSetEditor::_on_tile_list_selected(int p_index) { - if (get_current_tile() >= 0) { +void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) { + option = p_index; + switch (option) { + case TOOL_TILESET_ADD_TEXTURE: { + texture_dialog->popup_centered_ratio(); + } break; + case TOOL_TILESET_REMOVE_TEXTURE: { + if (get_current_texture().is_valid()) { + cd->set_text(TTR("Remove Selected Textue and ALL TILES wich uses it?")); + cd->popup_centered(Size2(300, 60)); + } else { + err_dialog->set_text(TTR("You haven't selected a texture to remove.")); + err_dialog->popup_centered(Size2(300, 60)); + } + } break; + case TOOL_TILESET_CREATE_SCENE: { + + cd->set_text(TTR("Create from scene?")); + cd->popup_centered(Size2(300, 60)); + } break; + case TOOL_TILESET_MERGE_SCENE: { + + cd->set_text(TTR("Merge from scene?")); + cd->popup_centered(Size2(300, 60)); + } break; + } +} + +void TileSetEditor::_on_tileset_toolbar_confirm() { + switch (option) { + case TOOL_TILESET_REMOVE_TEXTURE: { + RID current_rid = get_current_texture()->get_rid(); + List<int> ids; + tileset->get_tile_list(&ids); + for (List<int>::Element *E = ids.front(); E; E = E->next()) { + if (tileset->tile_get_texture(E->get())->get_rid() == current_rid) { + tileset->remove_tile(E->get()); + } + } + texture_list->remove_item(texture_list->find_metadata(current_rid)); + texture_map.erase(current_rid); + _on_texture_list_selected(-1); + } break; + case TOOL_TILESET_MERGE_SCENE: + case TOOL_TILESET_CREATE_SCENE: { + + EditorNode *en = editor; + Node *scene = en->get_edited_scene(); + if (!scene) + break; + _import_scene(scene, tileset, option == TOOL_TILESET_MERGE_SCENE); + + edit(tileset); + } break; + } +} + +void TileSetEditor::_on_texture_list_selected(int p_index) { + if (get_current_texture().is_valid()) { current_item_index = p_index; - preview->set_texture(tileset->tile_get_texture(get_current_tile())); - preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); - preview->set_region_rect(tileset->tile_get_region(get_current_tile())); - workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size); + preview->set_texture(get_current_texture()); + workspace->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); + workspace_container->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); + workspace_overlay->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); update_workspace_tile_mode(); } else { current_item_index = -1; preview->set_texture(NULL); workspace->set_custom_minimum_size(Size2i()); + update_workspace_tile_mode(); } - texture_region_editor->selected_tile = get_current_tile(); - texture_region_editor->_edit_region(); - helper->selected_tile = get_current_tile(); - helper->_change_notify(""); + set_current_tile(-1); workspace->update(); } +void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) { + int invalid_count = 0; + for (int i = 0; i < p_paths.size(); i++) { + Ref<Texture> t = Ref<Texture>(ResourceLoader::load(p_paths[i])); + if (texture_map.has(t->get_rid())) { + invalid_count++; + } else { + texture_list->add_item(t->get_path().get_file()); + texture_map.insert(t->get_rid(), t); + texture_list->set_item_metadata(texture_list->get_item_count() - 1, t->get_rid()); + } + } + update_texture_list_icon(); + texture_list->select(texture_list->get_item_count() - 1); + _on_texture_list_selected(texture_list->get_item_count() - 1); + if (invalid_count > 0) { + err_dialog->set_text(String::num(invalid_count, 0) + TTR(" file(s) was not added because was already on the list.")); + err_dialog->popup_centered(Size2(300, 60)); + } +} + void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { edit_mode = (EditMode)p_edit_mode; switch (edit_mode) { + case EDITMODE_REGION: { + tools[TOOL_SELECT]->show(); + tools[BITMASK_COPY]->hide(); + tools[BITMASK_PASTE]->hide(); + tools[BITMASK_CLEAR]->hide(); + tools[SHAPE_NEW_POLYGON]->hide(); + if (workspace_mode == WORKSPACE_EDIT) + tools[SHAPE_DELETE]->show(); + else + tools[SHAPE_DELETE]->hide(); + tools[SHAPE_KEEP_INSIDE_TILE]->hide(); + tools[TOOL_GRID_SNAP]->show(); + + tools[TOOL_SELECT]->set_pressed(true); + tools[TOOL_SELECT]->set_tooltip(TTR("Drag handles to edit Rect.\nClick on another Tile to edit it.")); + spin_priority->hide(); + } break; case EDITMODE_BITMASK: { - tool_containers[TOOLBAR_DUMMY]->show(); - tool_containers[TOOLBAR_BITMASK]->show(); - tool_containers[TOOLBAR_SHAPE]->hide(); + tools[TOOL_SELECT]->show(); + tools[BITMASK_COPY]->show(); + tools[BITMASK_PASTE]->show(); + tools[BITMASK_CLEAR]->show(); + tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_DELETE]->hide(); + tools[SHAPE_KEEP_INSIDE_TILE]->hide(); + tools[TOOL_GRID_SNAP]->hide(); + tools[TOOL_SELECT]->set_pressed(true); - tools[TOOL_SELECT]->set_tooltip(TTR("LMB: set bit on.\nRMB: set bit off.")); + tools[TOOL_SELECT]->set_tooltip(TTR("LMB: set bit on.\nRMB: set bit off.\nClick on another Tile to edit it.")); spin_priority->hide(); } break; case EDITMODE_COLLISION: case EDITMODE_NAVIGATION: case EDITMODE_OCCLUSION: { - tool_containers[TOOLBAR_DUMMY]->show(); - tool_containers[TOOLBAR_BITMASK]->hide(); - tool_containers[TOOLBAR_SHAPE]->show(); - tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.")); + tools[TOOL_SELECT]->show(); + tools[BITMASK_COPY]->hide(); + tools[BITMASK_PASTE]->hide(); + tools[BITMASK_CLEAR]->hide(); + tools[SHAPE_NEW_POLYGON]->show(); + tools[SHAPE_DELETE]->show(); + tools[SHAPE_KEEP_INSIDE_TILE]->show(); + tools[TOOL_GRID_SNAP]->show(); + + tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.\nClick on another Tile to edit it.")); spin_priority->hide(); - select_coord(edited_shape_coord); } break; default: { - tool_containers[TOOLBAR_DUMMY]->show(); - tool_containers[TOOLBAR_BITMASK]->hide(); - tool_containers[TOOLBAR_SHAPE]->hide(); + tools[TOOL_SELECT]->show(); + tools[BITMASK_COPY]->hide(); + tools[BITMASK_PASTE]->hide(); + tools[BITMASK_CLEAR]->hide(); + tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_DELETE]->hide(); + tools[SHAPE_KEEP_INSIDE_TILE]->hide(); + tools[TOOL_GRID_SNAP]->show(); if (edit_mode == EDITMODE_ICON) { - tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.")); + tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.\nClick on another Tile to edit it.")); spin_priority->hide(); } else { - tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its priority.")); + tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its priority.\nClick on another Tile to edit it.")); spin_priority->show(); } } break; @@ -634,25 +650,52 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { workspace->update(); } +void TileSetEditor::_on_workspace_mode_changed(int p_workspace_mode) { + workspace_mode = (WorkspaceMode)p_workspace_mode; + if (p_workspace_mode == WORKSPACE_EDIT) { + update_workspace_tile_mode(); + } else { + for (int i = 0; i < EDITMODE_MAX; i++) { + tool_editmode[i]->hide(); + } + tool_editmode[EDITMODE_REGION]->show(); + tool_editmode[EDITMODE_REGION]->set_pressed(true); + _on_edit_mode_changed(EDITMODE_REGION); + } +} + void TileSetEditor::_on_workspace_draw() { - if (get_current_tile() >= 0 && !tileset.is_null()) { + const Color COLOR_AUTOTILE = Color(0.266373, 0.565288, 0.988281); + const Color COLOR_SINGLE = Color(0.988281, 0.909323, 0.266373); + const Color COLOR_ATLAS = Color(0.78653, 0.812835, 0.832031); + + if (tileset.is_null()) + return; + if (!get_current_texture().is_valid()) + return; + + draw_highlight_current_tile(); + + draw_grid_snap(); + if (get_current_tile() >= 0) { int spacing = tileset->autotile_get_spacing(get_current_tile()); Vector2 size = tileset->autotile_get_size(get_current_tile()); Rect2i region = tileset->tile_get_region(get_current_tile()); - Color c(0.347214, 0.722656, 0.617063); switch (edit_mode) { case EDITMODE_ICON: { Vector2 coord = tileset->autotile_get_icon_coordinate(get_current_tile()); - draw_highlight_tile(coord); + draw_highlight_subtile(coord); } break; case EDITMODE_BITMASK: { - c = Color(1, 0, 0, 0.5); + Color c(1, 0, 0, 0.5); for (float x = 0; x < region.size.x / (spacing + size.x); x++) { for (float y = 0; y < region.size.y / (spacing + size.y); y++) { Vector2 coord(x, y); Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); + anchor += WORKSPACE_MARGIN; + anchor += region.position; uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (mask & TileSet::BIND_TOPLEFT) { @@ -702,9 +745,9 @@ void TileSetEditor::_on_workspace_draw() { case EDITMODE_COLLISION: case EDITMODE_OCCLUSION: case EDITMODE_NAVIGATION: { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { Vector2 coord = edited_shape_coord; - draw_highlight_tile(coord); + draw_highlight_subtile(coord); } draw_polygon_shapes(); draw_grid_snap(); @@ -723,89 +766,334 @@ void TileSetEditor::_on_workspace_draw() { } } spin_priority->set_suffix(" / " + String::num(total, 0)); - draw_highlight_tile(edited_shape_coord, queue_others); + draw_highlight_subtile(edited_shape_coord, queue_others); } break; } - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { - float j = -size.x; //make sure to draw at 0 - while (j < region.size.x) { - j += size.x; - if (spacing <= 0) { - workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c); - } else { - workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c); - } - j += spacing; - } - j = -size.y; //make sure to draw at 0 - while (j < region.size.y) { - j += size.y; - if (spacing <= 0) { - workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c); - } else { - workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c); - } - j += spacing; + draw_tile_subdivision(get_current_tile(), Color(0.347214, 0.722656, 0.617063)); + } + + RID current_texture_rid = get_current_texture()->get_rid(); + List<int> *tiles = new List<int>(); + tileset->get_tile_list(tiles); + for (List<int>::Element *E = tiles->front(); E; E = E->next()) { + int t_id = E->get(); + if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid && (t_id != get_current_tile() || edit_mode != EDITMODE_REGION)) { + Rect2i region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + draw_tile_subdivision(t_id, Color(0.347214, 0.722656, 0.617063, 0.5)); + workspace->draw_rect(region, c, false); + } + } + if (edit_mode == EDITMODE_REGION) { + if (workspace_mode != WORKSPACE_EDIT) { + Rect2i region = edited_region; + Color c; + if (workspace_mode == WORKSPACE_CREATE_SINGLE) + c = COLOR_SINGLE; + else if (workspace_mode == WORKSPACE_CREATE_AUTOTILE) + c = COLOR_AUTOTILE; + else if (workspace_mode == WORKSPACE_CREATE_ATLAS) + c = COLOR_ATLAS; + workspace->draw_rect(region, c, false); + draw_edited_region_subdivision(); + } else { + int t_id = get_current_tile(); + Rect2i region; + if (draw_edited_region) + region = edited_region; + else { + region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; } + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + if (draw_edited_region) + draw_edited_region_subdivision(); + else + draw_tile_subdivision(t_id, Color(0.347214, 0.722656, 0.617063, 1)); + workspace->draw_rect(region, c, false); } } workspace_overlay->update(); } +void TileSetEditor::_on_workspace_process() { + float a = tile_names_opacity; + if (Input::get_singleton()->is_key_pressed(KEY_ALT) || tools[VISIBLE_INFO]->is_pressed()) { + a += get_tree()->get_idle_process_time() * 2; + } else { + a -= get_tree()->get_idle_process_time() * 2; + } + + a = CLAMP(a, 0, 1); + if (a != tile_names_opacity) + workspace_overlay->update(); + tile_names_opacity = a; +} + void TileSetEditor::_on_workspace_overlay_draw() { + if (!tileset.is_valid()) + return; + if (!get_current_texture().is_valid()) + return; + + const Color COLOR_AUTOTILE = Color(0.266373, 0.565288, 0.988281); + const Color COLOR_SINGLE = Color(0.988281, 0.909323, 0.266373); + const Color COLOR_ATLAS = Color(0.78653, 0.812835, 0.832031); + + if (tile_names_opacity > 0) { + RID current_texture_rid = get_current_texture()->get_rid(); + List<int> *tiles = new List<int>(); + tileset->get_tile_list(tiles); + for (List<int>::Element *E = tiles->front(); E; E = E->next()) { + int t_id = E->get(); + if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid) { + Rect2i region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + region.position *= workspace->get_scale().x; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + c.a = tile_names_opacity; + Ref<Font> font = get_font("font", "Label"); + region.set_size(font->get_string_size(tileset->tile_get_name(t_id))); + workspace_overlay->draw_rect(region, c); + region.position.y += region.size.y - 2; + c = Color(0.1, 0.1, 0.1, tile_names_opacity); + workspace_overlay->draw_string(font, region.position, tileset->tile_get_name(t_id), c); + } + } + } + int t_id = get_current_tile(); - if (t_id < 0 || !draw_handles) + if (t_id < 0) return; Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); - - for (int i = 0; i < current_shape.size(); i++) { - workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5); + if (draw_handles) { + for (int i = 0; i < current_shape.size(); i++) { + workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5); + } } } #define MIN_DISTANCE_SQUARED 6 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { + if (tileset.is_null()) + return; + if (!get_current_texture().is_valid()) + return; - if (get_current_tile() >= 0 && !tileset.is_null()) { - Ref<InputEventMouseButton> mb = p_ie; - Ref<InputEventMouseMotion> mm = p_ie; + static bool dragging; + static bool erasing; + draw_edited_region = false; - static bool dragging; - static bool erasing; + Rect2 current_tile_region = Rect2(); + if (get_current_tile() >= 0) { + current_tile_region = tileset->tile_get_region(get_current_tile()); + } + current_tile_region.position += WORKSPACE_MARGIN; + + Ref<InputEventMouseButton> mb = p_ie; + Ref<InputEventMouseMotion> mm = p_ie; + + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (!current_tile_region.has_point(mb->get_position())) { + List<int> *tiles = new List<int>(); + tileset->get_tile_list(tiles); + for (List<int>::Element *E = tiles->front(); E; E = E->next()) { + int t_id = E->get(); + if (get_current_texture()->get_rid() == tileset->tile_get_texture(t_id)->get_rid()) { + Rect2 r = tileset->tile_get_region(t_id); + r.position += WORKSPACE_MARGIN; + if (r.has_point(mb->get_position())) { + set_current_tile(t_id); + workspace->update(); + workspace_overlay->update(); + return; + } + } + } + } + } + } + // Drag Middle Mouse + if (mm.is_valid()) { + if (mm->get_button_mask() & BUTTON_MASK_MIDDLE) { + Vector2 dragged(mm->get_relative().x, mm->get_relative().y); + scroll->set_h_scroll(scroll->get_h_scroll() - dragged.x * workspace->get_scale().x); + scroll->set_v_scroll(scroll->get_v_scroll() - dragged.y * workspace->get_scale().x); + } + } - int spacing = tileset->autotile_get_spacing(get_current_tile()); - Vector2 size = tileset->autotile_get_size(get_current_tile()); - switch (edit_mode) { - case EDITMODE_ICON: { - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); - tileset->autotile_set_icon_coordinate(get_current_tile(), coord); - Rect2 region = tileset->tile_get_region(get_current_tile()); - region.size = size; - coord.x *= (spacing + size.x); - coord.y *= (spacing + size.y); - region.position += coord; - tile_list->set_item_icon_region(current_item_index, region); - workspace->update(); + if (edit_mode == EDITMODE_REGION) { + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (get_current_tile() >= 0 || workspace_mode != WORKSPACE_EDIT) { + dragging = true; + region_from = mb->get_position(); + edited_region = Rect2(region_from, Size2()); + workspace->update(); + workspace_overlay->update(); + return; + } + } else if (dragging && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + dragging = false; + edited_region = Rect2(); + workspace->update(); + workspace_overlay->update(); + return; + } else if (dragging && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + dragging = false; + update_edited_region(mb->get_position()); + edited_region.position -= WORKSPACE_MARGIN; + if (!edited_region.has_no_area()) { + if (get_current_tile() >= 0 && workspace_mode == WORKSPACE_EDIT) { + tileset->tile_set_region(get_current_tile(), edited_region); + } else { + int t_id = tileset->get_last_unused_tile_id(); + tileset->create_tile(t_id); + tileset->tile_set_texture(t_id, get_current_texture()); + tileset->tile_set_region(t_id, edited_region); + tileset->tile_set_name(t_id, get_current_texture()->get_path().get_file() + " " + String::num(t_id, 0)); + if (workspace_mode != WORKSPACE_CREATE_SINGLE) { + tileset->autotile_set_size(t_id, snap_step); + tileset->autotile_set_spacing(t_id, snap_separation.x); + tileset->tile_set_tile_mode(t_id, workspace_mode == WORKSPACE_CREATE_AUTOTILE ? TileSet::AUTO_TILE : TileSet::ATLAS_TILE); + } + set_current_tile(t_id); + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); + _on_workspace_mode_changed(WORKSPACE_EDIT); } } - } break; - case EDITMODE_BITMASK: { - if (mb.is_valid()) { - if (mb->is_pressed()) { - if (dragging) { - return; + workspace->update(); + workspace_overlay->update(); + return; + } + } else if (mm.is_valid()) { + if (dragging) { + update_edited_region(mm->get_position()); + draw_edited_region = true; + workspace->update(); + workspace_overlay->update(); + return; + } + } + } + if (workspace_mode == WORKSPACE_EDIT) { + + if (get_current_tile() >= 0) { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + switch (edit_mode) { + case EDITMODE_ICON: { + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && current_tile_region.has_point(mb->get_position())) { + Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); + tileset->autotile_set_icon_coordinate(get_current_tile(), coord); + Rect2 region = tileset->tile_get_region(get_current_tile()); + region.size = size; + coord.x *= (spacing + size.x); + coord.y *= (spacing + size.y); + region.position += coord; + workspace->update(); + } + } + } break; + case EDITMODE_BITMASK: { + if (mb.is_valid()) { + if (mb->is_pressed()) { + if (dragging) { + return; + } + if ((mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) { + dragging = true; + erasing = (mb->get_button_index() == BUTTON_RIGHT); + Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); + Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); + pos = mb->get_position() - (pos + current_tile_region.position); + uint16_t bit = 0; + if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { + if (pos.x < size.x / 2) { + if (pos.y < size.y / 2) { + bit = TileSet::BIND_TOPLEFT; + } else { + bit = TileSet::BIND_BOTTOMLEFT; + } + } else { + if (pos.y < size.y / 2) { + bit = TileSet::BIND_TOPRIGHT; + } else { + bit = TileSet::BIND_BOTTOMRIGHT; + } + } + } else { + if (pos.x < size.x / 3) { + if (pos.y < size.y / 3) { + bit = TileSet::BIND_TOPLEFT; + } else if (pos.y > (size.y / 3) * 2) { + bit = TileSet::BIND_BOTTOMLEFT; + } else { + bit = TileSet::BIND_LEFT; + } + } else if (pos.x > (size.x / 3) * 2) { + if (pos.y < size.y / 3) { + bit = TileSet::BIND_TOPRIGHT; + } else if (pos.y > (size.y / 3) * 2) { + bit = TileSet::BIND_BOTTOMRIGHT; + } else { + bit = TileSet::BIND_RIGHT; + } + } else { + if (pos.y < size.y / 3) { + bit = TileSet::BIND_TOP; + } else if (pos.y > (size.y / 3) * 2) { + bit = TileSet::BIND_BOTTOM; + } else { + bit = TileSet::BIND_CENTER; + } + } + } + uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + if (erasing) { + mask &= ~bit; + } else { + mask |= bit; + } + tileset->autotile_set_bitmask(get_current_tile(), coord, mask); + workspace->update(); + } + } else { + if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { + dragging = false; + erasing = false; + } } - if (mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) { - dragging = true; - erasing = (mb->get_button_index() == BUTTON_RIGHT); - Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); + } + if (mm.is_valid()) { + if (dragging && current_tile_region.has_point(mm->get_position())) { + Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); - pos = mb->get_position() - pos; + pos = mm->get_position() - (pos + current_tile_region.position); uint16_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { @@ -857,269 +1145,198 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { tileset->autotile_set_bitmask(get_current_tile(), coord, mask); workspace->update(); } - } else { - if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { - dragging = false; - erasing = false; - } } - } - if (mm.is_valid()) { - if (dragging) { - Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y))); - Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); - pos = mm->get_position() - pos; - uint16_t bit = 0; - if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { - if (pos.x < size.x / 2) { - if (pos.y < size.y / 2) { - bit = TileSet::BIND_TOPLEFT; - } else { - bit = TileSet::BIND_BOTTOMLEFT; - } - } else { - if (pos.y < size.y / 2) { - bit = TileSet::BIND_TOPRIGHT; - } else { - bit = TileSet::BIND_BOTTOMRIGHT; - } - } - } else { - if (pos.x < size.x / 3) { - if (pos.y < size.y / 3) { - bit = TileSet::BIND_TOPLEFT; - } else if (pos.y > (size.y / 3) * 2) { - bit = TileSet::BIND_BOTTOMLEFT; - } else { - bit = TileSet::BIND_LEFT; - } - } else if (pos.x > (size.x / 3) * 2) { - if (pos.y < size.y / 3) { - bit = TileSet::BIND_TOPRIGHT; - } else if (pos.y > (size.y / 3) * 2) { - bit = TileSet::BIND_BOTTOMRIGHT; - } else { - bit = TileSet::BIND_RIGHT; - } - } else { - if (pos.y < size.y / 3) { - bit = TileSet::BIND_TOP; - } else if (pos.y > (size.y / 3) * 2) { - bit = TileSet::BIND_BOTTOM; - } else { - bit = TileSet::BIND_CENTER; - } - } - } - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - if (erasing) { - mask &= ~bit; - } else { - mask |= bit; - } - tileset->autotile_set_bitmask(get_current_tile(), coord, mask); - workspace->update(); + } break; + case EDITMODE_COLLISION: + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: + case EDITMODE_PRIORITY: { + Vector2 shape_anchor = Vector2(0, 0); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); } - } - } break; - case EDITMODE_COLLISION: - case EDITMODE_OCCLUSION: - case EDITMODE_NAVIGATION: - case EDITMODE_PRIORITY: { - Vector2 shape_anchor = Vector2(0, 0); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { - shape_anchor = edited_shape_coord; - shape_anchor.x *= (size.x + spacing); - shape_anchor.y *= (size.y + spacing); - } - if (tools[TOOL_SELECT]->is_pressed()) { - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) { - for (int i = 0; i < current_shape.size(); i++) { - if ((current_shape[i] - mb->get_position()).length_squared() <= MIN_DISTANCE_SQUARED) { - dragging_point = i; - workspace->update(); - return; + shape_anchor += current_tile_region.position; + if (tools[TOOL_SELECT]->is_pressed()) { + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) { + for (int i = 0; i < current_shape.size(); i++) { + if ((current_shape[i] - mb->get_position()).length_squared() <= MIN_DISTANCE_SQUARED) { + dragging_point = i; + workspace->update(); + return; + } } } - } - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { - Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); - if (edited_shape_coord != coord) { - edited_shape_coord = coord; - edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord); - edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord); - Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); - bool found_collision_shape = false; - for (int i = 0; i < sd.size(); i++) { - if (sd[i].autotile_coord == coord) { - edited_collision_shape = sd[i].shape; - found_collision_shape = true; - break; + if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) && current_tile_region.has_point(mb->get_position())) { + Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); + if (edited_shape_coord != coord) { + edited_shape_coord = coord; + edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord); + edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord); + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + bool found_collision_shape = false; + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == coord) { + edited_collision_shape = sd[i].shape; + found_collision_shape = true; + break; + } } + if (!found_collision_shape) + edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); + select_coord(edited_shape_coord); } - if (!found_collision_shape) - edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); - select_coord(edited_shape_coord); } - } - workspace->update(); - } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (edit_mode == EDITMODE_COLLISION) { - if (dragging_point >= 0) { - dragging_point = -1; + workspace->update(); + } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (edit_mode == EDITMODE_COLLISION) { + if (dragging_point >= 0) { + dragging_point = -1; - Vector<Vector2> points; + Vector<Vector2> points; - for (int i = 0; i < current_shape.size(); i++) { - Vector2 p = current_shape[i]; - if (tools[SHAPE_GRID_SNAP]->is_pressed() || tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) { - p = snap_point(p); + for (int i = 0; i < current_shape.size(); i++) { + Vector2 p = current_shape[i]; + if (tools[TOOL_GRID_SNAP]->is_pressed() || tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) { + p = snap_point(p); + } + points.push_back(p - shape_anchor); } - points.push_back(p - shape_anchor); - } - edited_collision_shape->set_points(points); + edited_collision_shape->set_points(points); - workspace->update(); - } - } else if (edit_mode == EDITMODE_OCCLUSION) { - if (dragging_point >= 0) { - dragging_point = -1; - - PoolVector<Vector2> polygon; - polygon.resize(current_shape.size()); - PoolVector<Vector2>::Write w = polygon.write(); - - for (int i = 0; i < current_shape.size(); i++) { - w[i] = current_shape[i] - shape_anchor; + workspace->update(); } + } else if (edit_mode == EDITMODE_OCCLUSION) { + if (dragging_point >= 0) { + dragging_point = -1; - w = PoolVector<Vector2>::Write(); - edited_occlusion_shape->set_polygon(polygon); + PoolVector<Vector2> polygon; + polygon.resize(current_shape.size()); + PoolVector<Vector2>::Write w = polygon.write(); - workspace->update(); - } - } else if (edit_mode == EDITMODE_NAVIGATION) { - if (dragging_point >= 0) { - dragging_point = -1; + for (int i = 0; i < current_shape.size(); i++) { + w[i] = current_shape[i] - shape_anchor; + } - PoolVector<Vector2> polygon; - Vector<int> indices; - polygon.resize(current_shape.size()); - PoolVector<Vector2>::Write w = polygon.write(); + w = PoolVector<Vector2>::Write(); + edited_occlusion_shape->set_polygon(polygon); - for (int i = 0; i < current_shape.size(); i++) { - w[i] = current_shape[i] - shape_anchor; - indices.push_back(i); + workspace->update(); } + } else if (edit_mode == EDITMODE_NAVIGATION) { + if (dragging_point >= 0) { + dragging_point = -1; + + PoolVector<Vector2> polygon; + Vector<int> indices; + polygon.resize(current_shape.size()); + PoolVector<Vector2>::Write w = polygon.write(); + + for (int i = 0; i < current_shape.size(); i++) { + w[i] = current_shape[i] - shape_anchor; + indices.push_back(i); + } - w = PoolVector<Vector2>::Write(); - edited_navigation_shape->set_vertices(polygon); - edited_navigation_shape->add_polygon(indices); - - workspace->update(); - } - } - } - } else if (mm.is_valid()) { - if (dragging_point >= 0) { - current_shape.set(dragging_point, snap_point(mm->get_position())); - workspace->update(); - } - } - } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) { + w = PoolVector<Vector2>::Write(); + edited_navigation_shape->set_vertices(polygon); + edited_navigation_shape->add_polygon(indices); - if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - Vector2 pos = mb->get_position(); - pos = snap_point(pos); - if (creating_shape) { - if (current_shape.size() > 0) { - if ((pos - current_shape[0]).length_squared() <= MIN_DISTANCE_SQUARED) { - if (current_shape.size() > 2) { - close_shape(shape_anchor); - workspace->update(); - return; - } + workspace->update(); } } - current_shape.push_back(pos); + } + } else if (mm.is_valid()) { + if (dragging_point >= 0) { + current_shape.set(dragging_point, snap_point(mm->get_position())); workspace->update(); - } else { - int t_id = get_current_tile(); - if (t_id >= 0) { - if (edit_mode == EDITMODE_COLLISION) { - Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); - for (int i = 0; i < sd.size(); i++) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || sd[i].autotile_coord == edited_shape_coord) { - Ref<ConvexPolygonShape2D> shape = sd[i].shape; - - if (!shape.is_null()) { - sd.remove(i); - tileset->tile_set_shapes(get_current_tile(), sd); - edited_collision_shape = Ref<Shape2D>(); - workspace->update(); - } - break; + } + } + } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) { + + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Vector2 pos = mb->get_position(); + pos = snap_point(pos); + if (creating_shape) { + if (current_shape.size() > 0) { + if ((pos - current_shape[0]).length_squared() <= MIN_DISTANCE_SQUARED) { + if (current_shape.size() > 2) { + close_shape(shape_anchor); + workspace->update(); + return; } } - } else if (edit_mode == EDITMODE_OCCLUSION) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { - Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id); - for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); + } + current_shape.push_back(pos); + workspace->update(); + } else { + int t_id = get_current_tile(); + if (t_id >= 0) { + if (edit_mode == EDITMODE_COLLISION) { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); + for (int i = 0; i < sd.size(); i++) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || sd[i].autotile_coord == edited_shape_coord) { + Ref<ConvexPolygonShape2D> shape = sd[i].shape; + + if (!shape.is_null()) { + sd.remove(i); + tileset->tile_set_shapes(get_current_tile(), sd); + edited_collision_shape = Ref<Shape2D>(); + workspace->update(); + } break; } } - } else - tileset->tile_set_light_occluder(t_id, Ref<OccluderPolygon2D>()); + } else if (edit_mode == EDITMODE_OCCLUSION) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id); + for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) { + if (E->key() == edited_shape_coord) { + tileset->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord); + break; + } + } + } else + tileset->tile_set_light_occluder(t_id, Ref<OccluderPolygon2D>()); - edited_occlusion_shape = Ref<OccluderPolygon2D>(); - workspace->update(); - } else if (edit_mode == EDITMODE_NAVIGATION) { - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { - Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); - for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { - if (E->key() == edited_shape_coord) { - tileset->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord); - break; + edited_occlusion_shape = Ref<OccluderPolygon2D>(); + workspace->update(); + } else if (edit_mode == EDITMODE_NAVIGATION) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id); + for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) { + if (E->key() == edited_shape_coord) { + tileset->autotile_set_navigation_polygon(t_id, Ref<NavigationPolygon>(), edited_shape_coord); + break; + } } - } - } else - tileset->tile_set_navigation_polygon(t_id, Ref<NavigationPolygon>()); - edited_navigation_shape = Ref<NavigationPolygon>(); - workspace->update(); + } else + tileset->tile_set_navigation_polygon(t_id, Ref<NavigationPolygon>()); + edited_navigation_shape = Ref<NavigationPolygon>(); + workspace->update(); + } } - } - creating_shape = true; - current_shape.resize(0); - current_shape.push_back(snap_point(pos)); + creating_shape = true; + current_shape.resize(0); + current_shape.push_back(snap_point(pos)); + } + } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT && current_shape.size() > 2) { + if (creating_shape) { + close_shape(shape_anchor); + } } - } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT && current_shape.size() > 2) { + } else if (mm.is_valid()) { if (creating_shape) { - close_shape(shape_anchor); + workspace->update(); } } - } else if (mm.is_valid()) { - if (creating_shape) { - workspace->update(); - } } - } - } break; - } - - //Drag Middle Mouse - if (mm.is_valid()) { - if (mm->get_button_mask() & BUTTON_MASK_MIDDLE) { - - Vector2 dragged(mm->get_relative().x, mm->get_relative().y); - scroll->set_h_scroll(scroll->get_h_scroll() - dragged.x * workspace->get_scale().x); - scroll->set_v_scroll(scroll->get_v_scroll() - dragged.y * workspace->get_scale().x); + } break; } } } @@ -1144,6 +1361,16 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { workspace->update(); } else { switch (edit_mode) { + case EDITMODE_REGION: { + if (workspace_mode == WORKSPACE_EDIT && get_current_tile() >= 0) { + tileset->remove_tile(get_current_tile()); + workspace->update(); + workspace_overlay->update(); + } + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); + workspace_mode = WORKSPACE_EDIT; + update_workspace_tile_mode(); + } break; case EDITMODE_COLLISION: { if (!edited_collision_shape.is_null()) { Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); @@ -1186,22 +1413,22 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { if (scale > 0.1) { scale /= 2; workspace->set_scale(Vector2(scale, scale)); - workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); - workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); } } else if (p_tool == ZOOM_1) { workspace->set_scale(Vector2(1, 1)); - workspace_container->set_custom_minimum_size(preview->get_region_rect().size); - workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size); + workspace_container->set_custom_minimum_size(workspace->get_rect().size); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size); } else if (p_tool == ZOOM_IN) { float scale = workspace->get_scale().x; scale *= 2; workspace->set_scale(Vector2(scale, scale)); - workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); - workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); } else if (p_tool == TOOL_SELECT) { if (creating_shape) { - //Cancel Creation + // Cancel Creation creating_shape = false; current_shape.resize(0); workspace->update(); @@ -1215,66 +1442,140 @@ void TileSetEditor::_on_priority_changed(float val) { } void TileSetEditor::_on_grid_snap_toggled(bool p_val) { - if (p_val) - hb_grid->show(); - else - hb_grid->hide(); + helper->set_snap_options_visible(p_val); workspace->update(); } -void TileSetEditor::_set_snap_step_x(float p_val) { - snap_step.x = p_val; +void TileSetEditor::_set_snap_step(Vector2 p_val) { + snap_step.x = CLAMP(p_val.x, 0, 256); + snap_step.y = CLAMP(p_val.y, 0, 256); workspace->update(); } -void TileSetEditor::_set_snap_step_y(float p_val) { - snap_step.y = p_val; +void TileSetEditor::_set_snap_off(Vector2 p_val) { + snap_offset.x = CLAMP(p_val.x, 0, 256 + WORKSPACE_MARGIN.x); + snap_offset.y = CLAMP(p_val.y, 0, 256 + WORKSPACE_MARGIN.y); workspace->update(); } -void TileSetEditor::_set_snap_off_x(float p_val) { - snap_offset.x = p_val; +void TileSetEditor::_set_snap_sep(Vector2 p_val) { + snap_separation.x = CLAMP(p_val.x, 0, 256); + snap_separation.y = CLAMP(p_val.y, 0, 256); workspace->update(); } -void TileSetEditor::_set_snap_off_y(float p_val) { - snap_offset.y = p_val; - workspace->update(); -} -void TileSetEditor::_set_snap_sep_x(float p_val) { - snap_separation.x = p_val; - workspace->update(); -} +void TileSetEditor::draw_highlight_current_tile() { -void TileSetEditor::_set_snap_sep_y(float p_val) { - snap_separation.y = p_val; - workspace->update(); + if (get_current_tile() >= 0) { + Rect2 region = tileset->tile_get_region(get_current_tile()); + region.position += WORKSPACE_MARGIN; + workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, region.position.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(0, region.position.y, region.position.x, region.size.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(region.position.x + region.size.x, region.position.y, workspace->get_rect().size.x - region.position.x - region.size.x, region.size.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(0, region.position.y + region.size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - region.size.y - region.position.y), Color(0.3, 0.3, 0.3, 0.3)); + } else { + workspace->draw_rect(Rect2(Point2(0, 0), workspace->get_rect().size), Color(0.3, 0.3, 0.3, 0.3)); + } } -void TileSetEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) { +void TileSetEditor::draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted) { Vector2 size = tileset->autotile_get_size(get_current_tile()); int spacing = tileset->autotile_get_spacing(get_current_tile()); Rect2 region = tileset->tile_get_region(get_current_tile()); coord.x *= (size.x + spacing); coord.y *= (size.y + spacing); - workspace->draw_rect(Rect2(0, 0, region.size.x, coord.y), Color(0.5, 0.5, 0.5, 0.5)); - workspace->draw_rect(Rect2(0, coord.y, coord.x, size.y), Color(0.5, 0.5, 0.5, 0.5)); - workspace->draw_rect(Rect2(coord.x + size.x, coord.y, region.size.x - coord.x - size.x, size.y), Color(0.5, 0.5, 0.5, 0.5)); - workspace->draw_rect(Rect2(0, coord.y + size.y, region.size.x, region.size.y - size.y - coord.y), Color(0.5, 0.5, 0.5, 0.5)); + coord += region.position; + coord += WORKSPACE_MARGIN; + workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, coord.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(0, coord.y, coord.x, size.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(coord.x + size.x, coord.y, workspace->get_rect().size.x - coord.x - size.x, size.y), Color(0.3, 0.3, 0.3, 0.3)); + workspace->draw_rect(Rect2(0, coord.y + size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - size.y - coord.y), Color(0.3, 0.3, 0.3, 0.3)); coord += Vector2(1, 1) / workspace->get_scale().x; workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0, 0), false); for (int i = 0; i < other_highlighted.size(); i++) { coord = other_highlighted[i]; coord.x *= (size.x + spacing); coord.y *= (size.y + spacing); + coord += region.position; + coord += WORKSPACE_MARGIN; coord += Vector2(1, 1) / workspace->get_scale().x; - workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0, 0), false); + workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0.5, 0.5), false); + } +} + +void TileSetEditor::draw_tile_subdivision(int p_id, Color p_color) const { + Color c = p_color; + if (tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE) { + Rect2 region = tileset->tile_get_region(p_id); + Size2 size = tileset->autotile_get_size(p_id); + int spacing = tileset->autotile_get_spacing(p_id); + float j = 0; + while (j < region.size.x) { + j += size.x; + if (spacing <= 0) { + workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(j, 0), region.position + WORKSPACE_MARGIN + Point2(j, region.size.y), c); + } else { + workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(j, 0), Size2(spacing, region.size.y)), c); + } + j += spacing; + } + j = 0; + while (j < region.size.y) { + j += size.y; + if (spacing <= 0) { + workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(0, j), region.position + WORKSPACE_MARGIN + Point2(region.size.x, j), c); + } else { + workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(0, j), Size2(region.size.x, spacing)), c); + } + j += spacing; + } + } +} + +void TileSetEditor::draw_edited_region_subdivision() const { + Color c = Color(0.347214, 0.722656, 0.617063, 1); + Rect2 region = edited_region; + Size2 size; + int spacing; + bool draw; + if (workspace_mode == WORKSPACE_EDIT) { + int p_id = get_current_tile(); + size = tileset->autotile_get_size(p_id); + spacing = tileset->autotile_get_spacing(p_id); + draw = tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE; + } else { + size = snap_step; + spacing = snap_separation.x; + draw = workspace_mode != WORKSPACE_CREATE_SINGLE; + } + if (draw) { + + float j = 0; + while (j < region.size.x) { + j += size.x; + if (spacing <= 0) { + workspace->draw_line(region.position + Point2(j, 0), region.position + Point2(j, region.size.y), c); + } else { + workspace->draw_rect(Rect2(region.position + Point2(j, 0), Size2(spacing, region.size.y)), c); + } + j += spacing; + } + j = 0; + while (j < region.size.y) { + j += size.y; + if (spacing <= 0) { + workspace->draw_line(region.position + Point2(0, j), region.position + Point2(region.size.x, j), c); + } else { + workspace->draw_rect(Rect2(region.position + Point2(0, j), Size2(region.size.x, spacing)), c); + } + j += spacing; + } } } void TileSetEditor::draw_grid_snap() { - if (tools[SHAPE_GRID_SNAP]->is_pressed()) { + if (tools[TOOL_GRID_SNAP]->is_pressed()) { Color grid_color = Color(0.39, 0, 1, 0.2f); Size2 s = workspace->get_size(); @@ -1328,7 +1629,7 @@ void TileSetEditor::draw_polygon_shapes() { for (int i = 0; i < sd.size(); i++) { Vector2 coord = Vector2(0, 0); Vector2 anchor = Vector2(0, 0); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) { + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { coord = sd[i].autotile_coord; anchor = tileset->autotile_get_size(t_id); anchor.x += tileset->autotile_get_spacing(t_id); @@ -1336,6 +1637,8 @@ void TileSetEditor::draw_polygon_shapes() { anchor.x *= coord.x; anchor.y *= coord.y; } + anchor += WORKSPACE_MARGIN; + anchor += tileset->tile_get_region(t_id).position; Ref<ConvexPolygonShape2D> shape = sd[i].shape; if (shape.is_valid()) { Color c_bg; @@ -1407,6 +1710,8 @@ void TileSetEditor::draw_polygon_shapes() { anchor.y += tileset->autotile_get_spacing(t_id); anchor.x *= coord.x; anchor.y *= coord.y; + anchor += WORKSPACE_MARGIN; + anchor += tileset->tile_get_region(t_id).position; Ref<OccluderPolygon2D> shape = E->value(); if (shape.is_valid()) { Color c_bg; @@ -1483,6 +1788,8 @@ void TileSetEditor::draw_polygon_shapes() { anchor.y += tileset->autotile_get_spacing(t_id); anchor.x *= coord.x; anchor.y *= coord.y; + anchor += WORKSPACE_MARGIN; + anchor += tileset->tile_get_region(t_id).position; Ref<NavigationPolygon> shape = E->value(); if (shape.is_valid()) { Color c_bg; @@ -1558,10 +1865,10 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { shape->set_points(segments); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) tileset->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord); else - tileset->tile_set_shape(get_current_tile(), 0, shape); + tileset->tile_add_shape(get_current_tile(), shape, Transform2D()); edited_collision_shape = shape; } @@ -1582,7 +1889,7 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { w = PoolVector<Vector2>::Write(); shape->set_polygon(polygon); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) tileset->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord); else tileset->tile_set_light_occluder(get_current_tile(), shape); @@ -1606,7 +1913,7 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { shape->set_vertices(polygon); shape->add_polygon(indices); - if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) tileset->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord); else tileset->tile_set_navigation_polygon(get_current_tile(), shape); @@ -1619,6 +1926,8 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { void TileSetEditor::select_coord(const Vector2 &coord) { current_shape = PoolVector2Array(); + Rect2 current_tile_region = tileset->tile_get_region(get_current_tile()); + current_tile_region.position += WORKSPACE_MARGIN; if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0)) edited_collision_shape = tileset->tile_get_shape(get_current_tile(), 0); @@ -1631,14 +1940,14 @@ void TileSetEditor::select_coord(const Vector2 &coord) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { for (int i = 0; i < edited_collision_shape->get_points().size(); i++) { - current_shape.push_back(edited_collision_shape->get_points()[i]); + current_shape.push_back(edited_collision_shape->get_points()[i] + current_tile_region.position); } } } else if (edit_mode == EDITMODE_OCCLUSION) { current_shape.resize(0); if (edited_occlusion_shape.is_valid()) { for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) { - current_shape.push_back(edited_occlusion_shape->get_polygon()[i]); + current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + current_tile_region.position); } } } else if (edit_mode == EDITMODE_NAVIGATION) { @@ -1647,7 +1956,7 @@ void TileSetEditor::select_coord(const Vector2 &coord) { if (edited_navigation_shape->get_polygon_count() > 0) { PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices(); for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) { - current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]]); + current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + current_tile_region.position); } } } @@ -1658,6 +1967,7 @@ void TileSetEditor::select_coord(const Vector2 &coord) { Vector2 shape_anchor = coord; shape_anchor.x *= (size.x + spacing); shape_anchor.y *= (size.y + spacing); + shape_anchor += current_tile_region.position; if (edit_mode == EDITMODE_COLLISION) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { @@ -1684,6 +1994,9 @@ void TileSetEditor::select_coord(const Vector2 &coord) { } } } + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); } Vector2 TileSetEditor::snap_point(const Vector2 &point) { @@ -1694,11 +2007,13 @@ Vector2 TileSetEditor::snap_point(const Vector2 &point) { Vector2 anchor = coord; anchor.x *= (tile_size.x + spacing); anchor.y *= (tile_size.y + spacing); + anchor += tileset->tile_get_region(get_current_tile()).position; + anchor += WORKSPACE_MARGIN; Rect2 region(anchor, tile_size); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) - region.position = Point2(0, 0); + region.position = tileset->tile_get_region(get_current_tile()).position + WORKSPACE_MARGIN; - if (tools[SHAPE_GRID_SNAP]->is_pressed()) { + if (tools[TOOL_GRID_SNAP]->is_pressed()) { p.x = Math::snap_scalar_seperation(snap_offset.x, snap_step.x, p.x, snap_separation.x); p.y = Math::snap_scalar_seperation(snap_offset.y, snap_step.y, p.y, snap_separation.y); } @@ -1715,211 +2030,332 @@ Vector2 TileSetEditor::snap_point(const Vector2 &point) { return p; } -void TileSetEditor::update_tile_list() { - int selected_tile = get_current_tile(); - - if (selected_tile < 0) - selected_tile = 0; +void TileSetEditor::update_texture_list() { + Ref<Texture> selected_texture = get_current_texture(); helper->set_tileset(tileset); - tile_list->clear(); List<int> ids; tileset->get_tile_list(&ids); for (List<int>::Element *E = ids.front(); E; E = E->next()) { - tile_list->add_item(tileset->tile_get_name(E->get())); - tile_list->set_item_metadata(tile_list->get_item_count() - 1, E->get()); - tile_list->set_item_icon(tile_list->get_item_count() - 1, tileset->tile_get_texture(E->get())); - Rect2 region = tileset->tile_get_region(E->get()); - if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) { - region.size = tileset->autotile_get_size(E->get()); - Vector2 pos = tileset->autotile_get_icon_coordinate(E->get()); - pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x); - pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y); - region.position += pos; + if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) { + texture_list->add_item(tileset->tile_get_texture(E->get())->get_path().get_file()); + texture_map.insert(tileset->tile_get_texture(E->get())->get_rid(), tileset->tile_get_texture(E->get())); + texture_list->set_item_metadata(texture_list->get_item_count() - 1, tileset->tile_get_texture(E->get())->get_rid()); } - tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region); - tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get())); } - if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) { - tile_list->select(selected_tile); - _on_tile_list_selected(selected_tile); + if (texture_list->get_item_count() > 0 && selected_texture.is_valid()) { + texture_list->select(texture_list->find_metadata(selected_texture->get_rid())); + if (texture_list->get_selected_items().size() > 0) + _on_texture_list_selected(texture_list->get_selected_items()[0]); + } else if (get_current_texture().is_valid()) { + _on_texture_list_selected(texture_list->find_metadata(get_current_texture()->get_rid())); + } else { + _on_texture_list_selected(-1); } + update_texture_list_icon(); helper->_change_notify(""); } -void TileSetEditor::update_tile_list_icon() { - List<int> ids; - tileset->get_tile_list(&ids); - int current_idx = 0; - for (List<int>::Element *E = ids.front(); E; E = E->next()) { - if (current_idx >= tile_list->get_item_count()) - break; - - Rect2 region = tileset->tile_get_region(E->get()); - if (tileset->tile_get_tile_mode(E->get()) == TileSet::AUTO_TILE) { - region.size = tileset->autotile_get_size(E->get()); - Vector2 pos = tileset->autotile_get_icon_coordinate(E->get()); - pos.x *= (tileset->autotile_get_spacing(E->get()) + region.size.x); - pos.y *= (tileset->autotile_get_spacing(E->get()) + region.size.y); - region.position += pos; - } - tile_list->set_item_metadata(current_idx, E->get()); - tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get())); - tile_list->set_item_icon_region(current_idx, region); - tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get())); - tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get())); - current_idx += 1; +void TileSetEditor::update_texture_list_icon() { + + for (int current_idx = 0; current_idx < texture_list->get_item_count(); current_idx++) { + RID rid = texture_list->get_item_metadata(current_idx); + texture_list->set_item_icon(current_idx, texture_map[rid]); + texture_list->set_item_icon_region(current_idx, Rect2(0, 0, 150, 100)); } - tile_list->update(); + texture_list->update(); } void TileSetEditor::update_workspace_tile_mode() { - if (get_current_tile() < 0) + + if (workspace_mode != WORKSPACE_EDIT) { + for (int i = 0; i < EDITMODE_MAX; i++) { + tool_editmode[i]->hide(); + } + tool_editmode[EDITMODE_REGION]->show(); + tool_editmode[EDITMODE_REGION]->set_pressed(true); + _on_edit_mode_changed(EDITMODE_REGION); + return; + } + + if (get_current_tile() < 0) { + for (int i = 0; i < EDITMODE_MAX; i++) { + tool_editmode[i]->hide(); + } + for (int i = 0; i < ZOOM_OUT; i++) { + tools[i]->hide(); + } return; + } + + for (int i = 0; i < EDITMODE_MAX; i++) { + tool_editmode[i]->show(); + } + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { tool_editmode[EDITMODE_COLLISION]->set_pressed(true); - _on_edit_mode_changed(EDITMODE_COLLISION); - } else { - select_coord(Vector2(0, 0)); + edit_mode = EDITMODE_COLLISION; } + select_coord(Vector2(0, 0)); tool_editmode[EDITMODE_ICON]->hide(); tool_editmode[EDITMODE_BITMASK]->hide(); tool_editmode[EDITMODE_PRIORITY]->hide(); - property_editor->hide(); + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + if (edit_mode == EDITMODE_ICON) + select_coord(tileset->autotile_get_icon_coordinate(get_current_tile())); + else + select_coord(edited_shape_coord); + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) { + if (tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { + tool_editmode[EDITMODE_COLLISION]->set_pressed(true); + edit_mode = EDITMODE_COLLISION; + } + if (edit_mode == EDITMODE_ICON) + select_coord(tileset->autotile_get_icon_coordinate(get_current_tile())); + else + select_coord(edited_shape_coord); + + tool_editmode[EDITMODE_BITMASK]->hide(); + tool_editmode[EDITMODE_PRIORITY]->hide(); + } + _on_edit_mode_changed(edit_mode); +} + +void TileSetEditor::update_edited_region(const Vector2 &end_point) { + edited_region = Rect2(region_from, Size2()); + if (tools[TOOL_GRID_SNAP]->is_pressed()) { + Vector2 grid_coord; + grid_coord.x = Math::floor((region_from.x - snap_offset.x) / (snap_step.x + snap_separation.x)); + grid_coord.y = Math::floor((region_from.y - snap_offset.y) / (snap_step.y + snap_separation.y)); + grid_coord.x *= (snap_step.x + snap_separation.x); + grid_coord.y *= (snap_step.y + snap_separation.y); + grid_coord += snap_offset; + edited_region.expand_to(grid_coord); + grid_coord += snap_step; + edited_region.expand_to(grid_coord); + grid_coord.x = Math::floor((end_point.x - snap_offset.x) / (snap_step.x + snap_separation.x)); + grid_coord.y = Math::floor((end_point.y - snap_offset.y) / (snap_step.y + snap_separation.y)); + grid_coord.x *= (snap_step.x + snap_separation.x); + grid_coord.y *= (snap_step.y + snap_separation.y); + grid_coord += snap_offset; + edited_region.expand_to(grid_coord); + grid_coord += snap_step; + if (grid_coord.x < end_point.x) + grid_coord.x += snap_separation.x; + if (grid_coord.y < end_point.y) + grid_coord.y += snap_separation.y; + edited_region.expand_to(grid_coord); } else { - tool_editmode[EDITMODE_ICON]->show(); - tool_editmode[EDITMODE_BITMASK]->show(); - tool_editmode[EDITMODE_PRIORITY]->show(); - property_editor->show(); + edited_region.expand_to(end_point); } } -int TileSetEditor::get_current_tile() { - if (tile_list->get_selected_items().size() == 0) - return -1; +int TileSetEditor::get_current_tile() const { + return current_tile; +} + +void TileSetEditor::set_current_tile(int p_id) { + if (current_tile != p_id) { + current_tile = p_id; + helper->_change_notify(""); + select_coord(Vector2(0, 0)); + update_workspace_tile_mode(); + } +} + +Ref<Texture> TileSetEditor::get_current_texture() { + if (texture_list->get_selected_items().size() == 0) + return Ref<Texture>(); else - return tile_list->get_item_metadata(tile_list->get_selected_items()[0]); + return texture_map[texture_list->get_item_metadata(texture_list->get_selected_items()[0])]; } -void TileSetEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) { +void TilesetEditorContext::set_tileset(const Ref<TileSet> &p_tileset) { tileset = p_tileset; } -bool TileSetEditorHelper::_set(const StringName &p_name, const Variant &p_value) { +void TilesetEditorContext::set_snap_options_visible(bool p_visible) { + snap_options_visible = p_visible; + _change_notify(""); +} - if (selected_tile < 0 || tileset.is_null()) - return false; +bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value) { String name = p_name.operator String(); - bool v = false; - if (name == "bitmask_mode") { - tileset->set(String::num(selected_tile, 0) + "/autotile/bitmask_mode", p_value, &v); - } else if (name.left(7) == "layout/") { - tileset->set(String::num(selected_tile, 0) + "/autotile" + name.right(6), p_value, &v); - } - if (v) { - tileset->_change_notify("autotile"); + + if (name == "options_offset") { + Vector2 snap = p_value; + tileset_editor->_set_snap_off(snap + WORKSPACE_MARGIN); + return true; + } else if (name == "options_step") { + Vector2 snap = p_value; + tileset_editor->_set_snap_step(snap); + return true; + } else if (name == "options_separation") { + Vector2 snap = p_value; + tileset_editor->_set_snap_sep(snap); + return true; + } else if (p_name.operator String().left(5) == "tile_") { + String name = p_name.operator String().right(5); + bool v = false; + + if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) + return false; + + if (name == "autotile_bitmask_mode") { + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v); + } else if (name == "subtile_size") { + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", p_value, &v); + } else if (name == "subtile_spacing") { + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", p_value, &v); + } else { + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, p_value, &v); + } + if (v) { + tileset->_change_notify(""); + tileset_editor->workspace->update(); + tileset_editor->workspace_overlay->update(); + } + return v; } - return v; -} -bool TileSetEditorHelper::_get(const StringName &p_name, Variant &r_ret) const { + tileset_editor->err_dialog->set_text(TTR("This property can't be changed.")); + tileset_editor->err_dialog->popup_centered(Size2(300, 60)); + return false; +} - if (selected_tile < 0 || tileset.is_null()) - return false; - if (!tileset->has_tile(selected_tile)) - return false; +bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const { String name = p_name.operator String(); bool v = false; - if (name == "bitmask_mode") { - r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile/bitmask_mode", &v); - } else if (name.left(7) == "layout/") { - r_ret = tileset->get(String::num(selected_tile, 0) + "/autotile" + name.right(6), &v); + + if (name == "options_offset") { + r_ret = tileset_editor->snap_offset - WORKSPACE_MARGIN; + v = true; + } else if (name == "options_step") { + r_ret = tileset_editor->snap_step; + v = true; + } else if (name == "options_separation") { + r_ret = tileset_editor->snap_separation; + v = true; + } else if (name.left(5) == "tile_") { + name = name.right(5); + + if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) + return false; + if (!tileset->has_tile(tileset_editor->get_current_tile())) + return false; + + if (name == "autotile_bitmask_mode") { + r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", &v); + } else if (name == "subtile_size") { + r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", &v); + } else if (name == "subtile_spacing") { + r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", &v); + } else { + r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, &v); + } + return v; + } else if (name == "selected_collision") { + r_ret = tileset_editor->edited_collision_shape; + v = true; + } else if (name == "selected_navigation") { + r_ret = tileset_editor->edited_navigation_shape; + v = true; + } else if (name == "selected_occlusion") { + r_ret = tileset_editor->edited_occlusion_shape; + v = true; } return v; } -void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const { - - if (selected_tile < 0 || tileset.is_null()) - return; +void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3 (minimal),3x3")); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "layout/tile_size")); - p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1")); + if (snap_options_visible) { + p_list->push_back(PropertyInfo(Variant::NIL, "Snap Options", PROPERTY_HINT_NONE, "options_", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_offset")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_step")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_separation")); + } + if (tileset_editor->get_current_tile() >= 0 && !tileset.is_null()) { + int id = tileset_editor->get_current_tile(); + p_list->push_back(PropertyInfo(Variant::NIL, "Selected Tile", PROPERTY_HINT_NONE, "tile_", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::STRING, "tile_name")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_tex_offset")); + p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial")); + p_list->push_back(PropertyInfo(Variant::COLOR, "tile_modulate")); + p_list->push_back(PropertyInfo(Variant::INT, "tile_tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE")); + if (tileset->tile_get_tile_mode(id) == TileSet::AUTO_TILE) { + p_list->push_back(PropertyInfo(Variant::INT, "tile_autotile_bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size")); + p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1")); + } else if (tileset->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size")); + p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1")); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_occluder_offset")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_navigation_offset")); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, "tile_z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1")); + } + if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_COLLISION && tileset_editor->edited_collision_shape.is_valid()) { + p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_collision", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_collision_shape->get_class())); + } + if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_NAVIGATION && tileset_editor->edited_navigation_shape.is_valid()) { + p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_navigation", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_navigation_shape->get_class())); + } + if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_OCCLUSION && tileset_editor->edited_occlusion_shape.is_valid()) { + p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_occlusion", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_occlusion_shape->get_class())); + } } -TileSetEditorHelper::TileSetEditorHelper(TileSetEditor *p_tileset_editor) { - +TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) { tileset_editor = p_tileset_editor; - selected_tile = -1; } void TileSetEditorPlugin::edit(Object *p_node) { if (Object::cast_to<TileSet>(p_node)) { tileset_editor->edit(Object::cast_to<TileSet>(p_node)); - tileset_editor->show(); - tileset_editor->texture_region_editor->edit(p_node); - } else - tileset_editor->hide(); + editor->get_inspector()->edit(tileset_editor->helper); + } } bool TileSetEditorPlugin::handles(Object *p_node) const { - return p_node->is_class("TileSet"); + return p_node->is_class("TileSet") || + p_node->is_class("TilesetEditorContext"); } void TileSetEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - tileset_editor->show(); - tileset_editor->menu->show(); tileset_editor_button->show(); - tileset_editor->side_panel->show(); if (tileset_editor_button->is_pressed()) { - tileset_editor->bottom_panel->show(); + tileset_editor->show(); } - texture_region_button->show(); - if (texture_region_button->is_pressed()) - tileset_editor->texture_region_editor->show(); + get_tree()->connect("idle_frame", tileset_editor, "_on_workspace_process"); } else { tileset_editor->hide(); - tileset_editor->menu->hide(); - tileset_editor->side_panel->hide(); - tileset_editor->bottom_panel->hide(); tileset_editor_button->hide(); - texture_region_button->hide(); - tileset_editor->texture_region_editor->hide(); + get_tree()->disconnect("idle_frame", tileset_editor, "_on_workspace_process"); } } TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { - + editor = p_node; tileset_editor = memnew(TileSetEditor(p_node)); - add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor); - tileset_editor->set_anchors_and_margins_preset(Control::PRESET_TOP_WIDE); - tileset_editor->set_end(Point2(0, 22)); - tileset_editor->hide(); - - tileset_editor->texture_region_editor = memnew(TextureRegionEditor(p_node)); - texture_region_button = p_node->add_bottom_panel_item(TTR("Texture Region"), tileset_editor->texture_region_editor); - texture_region_button->set_tooltip(TTR("Texture Region Editor")); - - tileset_editor->texture_region_editor->set_custom_minimum_size(Size2(0, 200)); - tileset_editor->texture_region_editor->hide(); - texture_region_button->hide(); + tileset_editor_button = + p_node->add_bottom_panel_item(TTR("Tile Set"), tileset_editor); + tileset_editor_button->set_tooltip(TTR("Tile Set Editor")); - add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tileset_editor->side_panel); - tileset_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - tileset_editor->side_panel->set_custom_minimum_size(Size2(200, 0)); - tileset_editor->side_panel->hide(); - tileset_editor_button = p_node->add_bottom_panel_item(TTR("Tile Set"), tileset_editor->bottom_panel); + tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); + tileset_editor->hide(); tileset_editor_button->hide(); } diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 4894d641a3..0c175e718c 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -33,21 +33,38 @@ #include "editor/editor_name_dialog.h" #include "editor/editor_node.h" -#include "editor/plugins/texture_region_editor_plugin.h" #include "scene/2d/sprite.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/tile_set.h" -class TileSetEditorHelper; +#define WORKSPACE_MARGIN Vector2(10, 10) +class TilesetEditorContext; -class TileSetEditor : public Control { +class TileSetEditor : public Panel { friend class TileSetEditorPlugin; - friend class TextureRegionEditor; + friend class TilesetEditorContext; - GDCLASS(TileSetEditor, Control); + GDCLASS(TileSetEditor, Panel) + + enum TextureToolButtons { + TOOL_TILESET_ADD_TEXTURE, + TOOL_TILESET_REMOVE_TEXTURE, + TOOL_TILESET_CREATE_SCENE, + TOOL_TILESET_MERGE_SCENE, + TOOL_TILESET_MAX + }; + + enum WorkspaceMode { + WORKSPACE_EDIT, + WORKSPACE_CREATE_SINGLE, + WORKSPACE_CREATE_AUTOTILE, + WORKSPACE_CREATE_ATLAS, + WORKSPACE_MODE_MAX + }; enum EditMode { + EDITMODE_REGION, EDITMODE_COLLISION, EDITMODE_OCCLUSION, EDITMODE_NAVIGATION, @@ -57,13 +74,6 @@ class TileSetEditor : public Control { EDITMODE_MAX }; - enum TileSetToolbar { - TOOLBAR_DUMMY, - TOOLBAR_BITMASK, - TOOLBAR_SHAPE, - TOOLBAR_MAX - }; - enum TileSetTools { TOOL_SELECT, BITMASK_COPY, @@ -71,17 +81,42 @@ class TileSetEditor : public Control { BITMASK_CLEAR, SHAPE_NEW_POLYGON, SHAPE_DELETE, - SHAPE_CREATE_FROM_BITMASK, - SHAPE_CREATE_FROM_NOT_BITMASK, SHAPE_KEEP_INSIDE_TILE, - SHAPE_GRID_SNAP, + TOOL_GRID_SNAP, ZOOM_OUT, ZOOM_1, ZOOM_IN, + VISIBLE_INFO, TOOL_MAX }; Ref<TileSet> tileset; + TilesetEditorContext *helper; + EditorNode *editor; + + ConfirmationDialog *cd; + AcceptDialog *err_dialog; + EditorFileDialog *texture_dialog; + + ItemList *texture_list; + int option; + ToolButton *tileset_toolbar_buttons[TOOL_TILESET_MAX]; + MenuButton *tileset_toolbar_tools; + Map<RID, Ref<Texture> > texture_map; + + bool creating_shape; + int dragging_point; + float tile_names_opacity; + Vector2 region_from; + Rect2 edited_region; + bool draw_edited_region; + Vector2 edited_shape_coord; + PoolVector2Array current_shape; + Map<Vector2, uint16_t> bitmask_map_copy; + + Vector2 snap_step; + Vector2 snap_offset; + Vector2 snap_separation; Ref<ConvexPolygonShape2D> edited_collision_shape; Ref<OccluderPolygon2D> edited_occlusion_shape; @@ -94,55 +129,19 @@ class TileSetEditor : public Control { bool draw_handles; Control *workspace_overlay; Control *workspace; + Button *tool_workspacemode[WORKSPACE_MODE_MAX]; Button *tool_editmode[EDITMODE_MAX]; - HBoxContainer *tool_containers[TOOLBAR_MAX]; HBoxContainer *toolbar; - HBoxContainer *hb_grid; ToolButton *tools[TOOL_MAX]; SpinBox *spin_priority; - SpinBox *sb_step_y; - SpinBox *sb_step_x; - SpinBox *sb_off_y; - SpinBox *sb_off_x; - SpinBox *sb_sep_y; - SpinBox *sb_sep_x; + WorkspaceMode workspace_mode; EditMode edit_mode; + int current_tile; - Vector2 snap_step; - Vector2 snap_offset; - Vector2 snap_separation; + void update_texture_list(); + void update_texture_list_icon(); - bool creating_shape; - int dragging_point; - Vector2 edited_shape_coord; - PoolVector2Array current_shape; - Map<Vector2, uint16_t> bitmask_map_copy; - - EditorNode *editor; - TextureRegionEditor *texture_region_editor; - Control *bottom_panel; - Control *side_panel; - ItemList *tile_list; - PropertyEditor *property_editor; - TileSetEditorHelper *helper; - - MenuButton *menu; - ConfirmationDialog *cd; - EditorNameDialog *nd; - AcceptDialog *err_dialog; - - enum { - - MENU_OPTION_ADD_ITEM, - MENU_OPTION_REMOVE_ITEM, - MENU_OPTION_CREATE_FROM_SCENE, - MENU_OPTION_MERGE_FROM_SCENE - }; - - int option; - void _menu_cbk(int p_option); - void _menu_confirm(); - void _name_dialog_confirm(const String &name); + Ref<Texture> get_current_texture(); static void _import_node(Node *p_node, Ref<TileSet> p_library); static void _import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge); @@ -150,7 +149,6 @@ class TileSetEditor : public Control { protected: static void _bind_methods(); void _notification(int p_what); - virtual void _changed_callback(Object *p_changed, const char *p_prop); public: void edit(const Ref<TileSet> &p_tileset); @@ -160,53 +158,61 @@ public: ~TileSetEditor(); private: - void _on_tile_list_selected(int p_index); + void _on_tileset_toolbar_button_pressed(int p_index); + void _on_tileset_toolbar_confirm(); + void _on_texture_list_selected(int p_index); + void _on_textures_added(const PoolStringArray &p_paths); void _on_edit_mode_changed(int p_edit_mode); + void _on_workspace_mode_changed(int p_workspace_mode); void _on_workspace_overlay_draw(); void _on_workspace_draw(); + void _on_workspace_process(); void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); void _on_priority_changed(float val); void _on_grid_snap_toggled(bool p_val); - void _set_snap_step_x(float p_val); - void _set_snap_step_y(float p_val); - void _set_snap_off_x(float p_val); - void _set_snap_off_y(float p_val); - void _set_snap_sep_x(float p_val); - void _set_snap_sep_y(float p_val); - - void initialize_bottom_editor(); - void draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>()); + void _set_snap_step(Vector2 p_val); + void _set_snap_off(Vector2 p_val); + void _set_snap_sep(Vector2 p_val); + + void draw_highlight_current_tile(); + void draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>()); + void draw_tile_subdivision(int p_id, Color p_color) const; + void draw_edited_region_subdivision() const; void draw_grid_snap(); void draw_polygon_shapes(); void close_shape(const Vector2 &shape_anchor); void select_coord(const Vector2 &coord); Vector2 snap_point(const Vector2 &point); - void update_tile_list(); - void update_tile_list_icon(); void update_workspace_tile_mode(); + void update_edited_region(const Vector2 &end_point); - int get_current_tile(); + int get_current_tile() const; + void set_current_tile(int p_id); }; -class TileSetEditorHelper : public Object { +class TilesetEditorContext : public Object { friend class TileSetEditor; - GDCLASS(TileSetEditorHelper, Object); + GDCLASS(TilesetEditorContext, Object); Ref<TileSet> tileset; TileSetEditor *tileset_editor; - int selected_tile; + bool snap_options_visible; public: void set_tileset(const Ref<TileSet> &p_tileset); +private: + void set_snap_options_visible(bool p_visible); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - TileSetEditorHelper(TileSetEditor *p_tileset_editor); +public: + TilesetEditorContext(TileSetEditor *p_tileset_editor); }; class TileSetEditorPlugin : public EditorPlugin { @@ -214,11 +220,9 @@ class TileSetEditorPlugin : public EditorPlugin { GDCLASS(TileSetEditorPlugin, EditorPlugin); TileSetEditor *tileset_editor; + Button *tileset_editor_button; EditorNode *editor; - ToolButton *tileset_editor_button; - ToolButton *texture_region_button; - public: virtual String get_name() const { return "TileSet"; } bool has_main_screen() const { return false; } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index b9b8b07a2e..9218fed907 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -190,7 +190,7 @@ void VisualShaderEditor::_update_graph() { } for (int i = 0; i < plugins.size(); i++) { - custom_editor = plugins[i]->create_editor(vsnode); + custom_editor = plugins.write[i]->create_editor(vsnode); if (custom_editor) { break; } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 0d06b71420..6af7779575 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -72,16 +72,26 @@ private: MESSAGE_SUCCESS }; + enum InputType { + PROJECT_PATH, + INSTALL_PATH + }; + Mode mode; Button *browse; + Button *install_browse; Button *create_dir; Container *name_container; Container *path_container; + Container *install_path_container; Label *msg; LineEdit *project_path; LineEdit *project_name; + LineEdit *install_path; TextureRect *status_rect; + TextureRect *install_status_rect; FileDialog *fdialog; + FileDialog *fdialog_install; String zip_path; String zip_title; AcceptDialog *dialog_error; @@ -89,10 +99,11 @@ private: String created_folder_path; - void set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS) { + void set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH) { msg->set_text(p_msg); - Ref<Texture> current_icon = status_rect->get_texture(); + Ref<Texture> current_path_icon = status_rect->get_texture(); + Ref<Texture> current_install_icon = install_status_rect->get_texture(); Ref<Texture> new_icon; switch (p_type) { @@ -119,8 +130,11 @@ private: } break; } - if (current_icon != new_icon) + if (current_path_icon != new_icon && input_type == PROJECT_PATH) { status_rect->set_texture(new_icon); + } else if (current_install_icon != new_icon && input_type == INSTALL_PATH) { + install_status_rect->set_texture(new_icon); + } set_size(Size2(500, 0) * EDSCALE); } @@ -128,11 +142,19 @@ private: String _test_path() { DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - String valid_path; + String valid_path, valid_install_path; if (d->change_dir(project_path->get_text()) == OK) { valid_path = project_path->get_text(); } else if (d->change_dir(project_path->get_text().strip_edges()) == OK) { valid_path = project_path->get_text().strip_edges(); + } else if (project_path->get_text().ends_with(".zip")) { + if (d->file_exists(project_path->get_text())) { + valid_path = project_path->get_text(); + } + } else if (project_path->get_text().strip_edges().ends_with(".zip")) { + if (d->file_exists(project_path->get_text().strip_edges())) { + valid_path = project_path->get_text().strip_edges(); + } } if (valid_path == "") { @@ -142,11 +164,94 @@ private: return ""; } + if (mode == MODE_IMPORT && valid_path.ends_with(".zip")) { + if (d->change_dir(install_path->get_text()) == OK) { + valid_install_path = install_path->get_text(); + } else if (d->change_dir(install_path->get_text().strip_edges()) == OK) { + valid_install_path = install_path->get_text().strip_edges(); + } + + if (valid_install_path == "") { + set_message(TTR("The path does not exist."), MESSAGE_ERROR, INSTALL_PATH); + memdelete(d); + get_ok()->set_disabled(true); + return ""; + } + } + if (mode == MODE_IMPORT || mode == MODE_RENAME) { if (valid_path != "" && !d->file_exists("project.godot")) { - set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR); + if (valid_path.ends_with(".zip")) { + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io); + if (!pkg) { + + set_message(TTR("Error opening package file, not in zip format."), MESSAGE_ERROR); + memdelete(d); + get_ok()->set_disabled(true); + unzClose(pkg); + return ""; + } + + int ret = unzGoToFirstFile(pkg); + while (ret == UNZ_OK) { + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); + + if (String(fname).ends_with("project.godot")) { + break; + } + + ret = unzGoToNextFile(pkg); + } + + if (ret == UNZ_END_OF_LIST_OF_FILE) { + set_message(TTR("Invalid '.zip' project file, does not contain a 'project.godot' file."), MESSAGE_ERROR); + memdelete(d); + get_ok()->set_disabled(true); + unzClose(pkg); + return ""; + } + + unzClose(pkg); + + // check if the specified install folder is empty, even though this is not an error, it is good to check here + d->list_dir_begin(); + bool is_empty = true; + String n = d->get_next(); + while (n != String()) { + if (n != "." && n != "..") { + is_empty = false; + break; + } + n = d->get_next(); + } + d->list_dir_end(); + + if (!is_empty) { + + set_message(TTR("Please choose an empty folder."), MESSAGE_WARNING, INSTALL_PATH); + memdelete(d); + get_ok()->set_disabled(true); + return ""; + } + + } else { + set_message(TTR("Please choose a 'project.godot' or '.zip' file."), MESSAGE_ERROR); + memdelete(d); + install_path_container->hide(); + get_ok()->set_disabled(true); + return ""; + } + + } else if (valid_path.ends_with("zip")) { + + set_message(TTR("Directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH); memdelete(d); get_ok()->set_disabled(true); return ""; @@ -159,7 +264,7 @@ private: bool is_empty = true; String n = d->get_next(); while (n != String()) { - if (!n.begins_with(".")) { // i don't know if this is enough to guarantee an empty dir + if (n != "." && n != "..") { // i don't know if this is enough to guarantee an empty dir is_empty = false; break; } @@ -177,6 +282,7 @@ private: } set_message(""); + set_message("", MESSAGE_SUCCESS, INSTALL_PATH); memdelete(d); get_ok()->set_disabled(false); return valid_path; @@ -214,9 +320,14 @@ private: if (mode == MODE_IMPORT) { if (p.ends_with("project.godot")) { p = p.get_base_dir(); + install_path_container->hide(); + get_ok()->set_disabled(false); + } else if (p.ends_with(".zip")) { + install_path->set_text(p.get_base_dir()); + install_path_container->show(); get_ok()->set_disabled(false); } else { - set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR); + set_message(TTR("Please choose a 'project.godot' or '.zip' file."), MESSAGE_ERROR); get_ok()->set_disabled(true); return; } @@ -224,7 +335,11 @@ private: String sp = p.simplify_path(); project_path->set_text(sp); _path_text_changed(sp); - get_ok()->call_deferred("grab_focus"); + if (p.ends_with(".zip")) { + install_path->call_deferred("grab_focus"); + } else { + get_ok()->call_deferred("grab_focus"); + } } void _path_selected(const String &p_path) { @@ -236,6 +351,14 @@ private: get_ok()->call_deferred("grab_focus"); } + void _install_path_selected(const String &p_path) { + String p = p_path; + String sp = p.simplify_path(); + install_path->set_text(sp); + _path_text_changed(sp); + get_ok()->call_deferred("grab_focus"); + } + void _browse_path() { fdialog->set_current_dir(project_path->get_text()); @@ -245,12 +368,19 @@ private: fdialog->set_mode(FileDialog::MODE_OPEN_FILE); fdialog->clear_filters(); fdialog->add_filter("project.godot ; " VERSION_NAME " Project"); + fdialog->add_filter("*.zip ; Zip File"); } else { fdialog->set_mode(FileDialog::MODE_OPEN_DIR); } fdialog->popup_centered_ratio(); } + void _browse_install_path() { + fdialog_install->set_current_dir(install_path->get_text()); + fdialog_install->set_mode(FileDialog::MODE_OPEN_DIR); + fdialog_install->popup_centered_ratio(); + } + void _create_folder() { if (project_name->get_text() == "" || created_folder_path != "" || project_name->get_text().ends_with(".") || project_name->get_text().ends_with(" ")) { @@ -328,7 +458,15 @@ private: } else { if (mode == MODE_IMPORT) { - // nothing to do + + if (project_path->get_text().ends_with(".zip")) { + + mode = MODE_INSTALL; + ok_pressed(); + + return; + } + } else { if (mode == MODE_NEW) { @@ -357,6 +495,11 @@ private: } else if (mode == MODE_INSTALL) { + if (project_path->get_text().ends_with(".zip")) { + dir = install_path->get_text(); + zip_path = project_path->get_text(); + } + FileAccess *src_f = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); @@ -444,7 +587,7 @@ private: dialog_error->set_text(msg); dialog_error->popup_centered_minsize(); - } else { + } else if (!project_path->get_text().ends_with(".zip")) { dialog_error->set_text(TTR("Package Installed Successfully!")); dialog_error->popup_centered_minsize(); } @@ -486,6 +629,9 @@ private: if (status_rect->get_texture() == get_icon("StatusError", "EditorIcons")) msg->show(); + + if (install_status_rect->get_texture() == get_icon("StatusError", "EditorIcons")) + msg->show(); } void _notification(int p_what) { @@ -503,6 +649,8 @@ protected: ClassDB::bind_method("_path_text_changed", &ProjectDialog::_path_text_changed); ClassDB::bind_method("_path_selected", &ProjectDialog::_path_selected); ClassDB::bind_method("_file_selected", &ProjectDialog::_file_selected); + ClassDB::bind_method("_install_path_selected", &ProjectDialog::_install_path_selected); + ClassDB::bind_method("_browse_install_path", &ProjectDialog::_browse_install_path); ADD_SIGNAL(MethodInfo("project_created")); ADD_SIGNAL(MethodInfo("project_renamed")); } @@ -530,12 +678,15 @@ public: project_path->set_editable(false); browse->hide(); + install_browse->hide(); set_title(TTR("Rename Project")); get_ok()->set_text(TTR("Rename")); name_container->show(); status_rect->hide(); msg->hide(); + install_path_container->hide(); + install_status_rect->hide(); get_ok()->set_disabled(false); ProjectSettings *current = memnew(ProjectSettings); @@ -575,14 +726,18 @@ public: project_path->set_editable(true); browse->set_disabled(false); browse->show(); + install_browse->set_disabled(false); + install_browse->show(); create_dir->show(); status_rect->show(); + install_status_rect->show(); msg->show(); if (mode == MODE_IMPORT) { set_title(TTR("Import Existing Project")); get_ok()->set_text(TTR("Import & Edit")); name_container->hide(); + install_path_container->hide(); project_path->grab_focus(); } else if (mode == MODE_NEW) { @@ -590,6 +745,7 @@ public: set_title(TTR("Create New Project")); get_ok()->set_text(TTR("Create & Edit")); name_container->show(); + install_path_container->hide(); project_name->grab_focus(); } else if (mode == MODE_INSTALL) { @@ -597,6 +753,7 @@ public: set_title(TTR("Install Project:") + " " + zip_title); get_ok()->set_text(TTR("Install & Edit")); name_container->hide(); + install_path_container->hide(); project_path->grab_focus(); } @@ -644,6 +801,20 @@ public: project_path->set_h_size_flags(SIZE_EXPAND_FILL); pphb->add_child(project_path); + install_path_container = memnew(VBoxContainer); + vb->add_child(install_path_container); + + l = memnew(Label); + l->set_text(TTR("Project Installation Path:")); + install_path_container->add_child(l); + + HBoxContainer *iphb = memnew(HBoxContainer); + install_path_container->add_child(iphb); + + install_path = memnew(LineEdit); + install_path->set_h_size_flags(SIZE_EXPAND_FILL); + iphb->add_child(install_path); + // status icon status_rect = memnew(TextureRect); status_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); @@ -654,17 +825,33 @@ public: browse->connect("pressed", this, "_browse_path"); pphb->add_child(browse); + // install status icon + install_status_rect = memnew(TextureRect); + install_status_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + iphb->add_child(install_status_rect); + + install_browse = memnew(Button); + install_browse->set_text(TTR("Browse")); + install_browse->connect("pressed", this, "_browse_install_path"); + iphb->add_child(install_browse); + msg = memnew(Label); msg->set_align(Label::ALIGN_CENTER); vb->add_child(msg); fdialog = memnew(FileDialog); fdialog->set_access(FileDialog::ACCESS_FILESYSTEM); + fdialog_install = memnew(FileDialog); + fdialog_install->set_access(FileDialog::ACCESS_FILESYSTEM); add_child(fdialog); + add_child(fdialog_install); project_name->connect("text_changed", this, "_text_changed"); project_path->connect("text_changed", this, "_path_text_changed"); + install_path->connect("text_changed", this, "_path_text_changed"); fdialog->connect("dir_selected", this, "_path_selected"); fdialog->connect("file_selected", this, "_file_selected"); + fdialog_install->connect("dir_selected", this, "_install_path_selected"); + fdialog_install->connect("file_selected", this, "_install_path_selected"); set_hide_on_ok(false); mode = MODE_NEW; @@ -1594,8 +1781,7 @@ ProjectManager::ProjectManager() { vb->add_constant_override("separation", 15 * EDSCALE); String cp; - cp.push_back(0xA9); - cp.push_back(0); + cp += 0xA9; OS::get_singleton()->set_window_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + cp + " 2007-2018 Juan Linietsky, Ariel Manzur & Godot Contributors"); HBoxContainer *top_hb = memnew(HBoxContainer); @@ -1836,18 +2022,6 @@ void ProjectListFilter::_setup_filters() { filter_option->add_item(TTR("Path")); } -void ProjectListFilter::_command(int p_command) { - switch (p_command) { - - case CMD_CLEAR_FILTER: { - if (search_box->get_text() != "") { - search_box->clear(); - emit_signal("filter_changed"); - } - } break; - } -} - void ProjectListFilter::_search_text_changed(const String &p_newtext) { emit_signal("filter_changed"); } @@ -1870,13 +2044,14 @@ void ProjectListFilter::_filter_option_selected(int p_idx) { void ProjectListFilter::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) - clear_search_button->set_icon(get_icon("Close", "EditorIcons")); + if (p_what == NOTIFICATION_ENTER_TREE) { + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + } } void ProjectListFilter::_bind_methods() { - ClassDB::bind_method(D_METHOD("_command"), &ProjectListFilter::_command); ClassDB::bind_method(D_METHOD("_search_text_changed"), &ProjectListFilter::_search_text_changed); ClassDB::bind_method(D_METHOD("_filter_option_selected"), &ProjectListFilter::_filter_option_selected); @@ -1901,8 +2076,4 @@ ProjectListFilter::ProjectListFilter() { search_box->connect("text_changed", this, "_search_text_changed"); search_box->set_h_size_flags(SIZE_EXPAND_FILL); add_child(search_box); - - clear_search_button = memnew(ToolButton); - clear_search_button->connect("pressed", this, "_command", make_binds(CMD_CLEAR_FILTER)); - add_child(clear_search_button); } diff --git a/editor/project_manager.h b/editor/project_manager.h index a9d23b1f71..ad21e00b0d 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -128,13 +128,8 @@ class ProjectListFilter : public HBoxContainer { private: friend class ProjectManager; - enum Command { - CMD_CLEAR_FILTER, - }; - OptionButton *filter_option; LineEdit *search_box; - ToolButton *clear_search_button; enum FilterOption { FILTER_NAME, @@ -142,7 +137,6 @@ private: }; FilterOption _current_filter; - void _command(int p_command); void _search_text_changed(const String &p_newtext); void _setup_filters(); void _filter_option_selected(int p_idx); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index e6ae2d64e7..d52538f67b 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -81,7 +81,8 @@ void ProjectSettingsEditor::_notification(int p_what) { globals_editor->edit(ProjectSettings::get_singleton()); search_button->set_icon(get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); action_add_error->add_color_override("font_color", get_color("error_color", "Editor")); @@ -119,7 +120,8 @@ void ProjectSettingsEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { search_button->set_icon(get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); action_add_error->add_color_override("font_color", get_color("error_color", "Editor")); popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), get_icon("Keyboard", "EditorIcons")); popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), get_icon("JoyButton", "EditorIcons")); @@ -1462,7 +1464,7 @@ void ProjectSettingsEditor::_update_translations() { t->set_editable(0, true); t->set_tooltip(0, l); t->set_checked(0, l_filter.has(l)); - translation_filter_treeitems[i] = t; + translation_filter_treeitems.write[i] = t; } } else { for (int i = 0; i < s; i++) { @@ -1502,7 +1504,7 @@ void ProjectSettingsEditor::_update_translations() { if (langnames.length() > 0) langnames += ","; langnames += names[i]; - translation_locales_idxs_remap[l_idx] = i; + translation_locales_idxs_remap.write[l_idx] = i; l_idx++; } } @@ -1591,15 +1593,6 @@ void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) { } } -void ProjectSettingsEditor::_clear_search_box() { - - if (search_box->get_text() == "") - return; - - search_box->clear(); - globals_editor->get_inspector()->update_tree(); -} - void ProjectSettingsEditor::set_plugins_page() { tab_container->set_current_tab(plugin_settings->get_index()); @@ -1662,7 +1655,6 @@ void ProjectSettingsEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_translation_filter_option_changed"), &ProjectSettingsEditor::_translation_filter_option_changed); ClassDB::bind_method(D_METHOD("_translation_filter_mode_changed"), &ProjectSettingsEditor::_translation_filter_mode_changed); - ClassDB::bind_method(D_METHOD("_clear_search_box"), &ProjectSettingsEditor::_clear_search_box); ClassDB::bind_method(D_METHOD("_toggle_search_bar"), &ProjectSettingsEditor::_toggle_search_bar); ClassDB::bind_method(D_METHOD("_copy_to_platform_about_to_show"), &ProjectSettingsEditor::_copy_to_platform_about_to_show); @@ -1753,10 +1745,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); search_bar->add_child(search_box); - clear_button = memnew(ToolButton); - search_bar->add_child(clear_button); - clear_button->connect("pressed", this, "_clear_search_box"); - globals_editor = memnew(SectionedInspector); props_base->add_child(globals_editor); globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 3b74ae1909..b738c4ae20 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -69,7 +69,6 @@ class ProjectSettingsEditor : public AcceptDialog { HBoxContainer *search_bar; Button *search_button; LineEdit *search_box; - ToolButton *clear_button; HBoxContainer *add_prop_bar; AcceptDialog *message; @@ -158,7 +157,6 @@ class ProjectSettingsEditor : public AcceptDialog { void _translation_filter_mode_changed(int p_mode); void _toggle_search_bar(bool p_pressed); - void _clear_search_box(); void _copy_to_platform_about_to_show(); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 7f46844f6c..b370a711e3 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -90,7 +90,7 @@ bool EditorResourceConversionPlugin::handles(const Ref<Resource> &p_resource) co return false; } -Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) { +Ref<Resource> EditorResourceConversionPlugin::convert(const Ref<Resource> &p_resource) const { if (get_script_instance()) return get_script_instance()->call("_convert", p_resource); @@ -4394,7 +4394,7 @@ PropertyEditor::PropertyEditor() { use_filter = false; subsection_selectable = false; property_selectable = false; - show_type_icons = EDITOR_DEF("interface/editor/show_type_icons", false); + show_type_icons = false; // TODO: need to reimplement it to work with the new inspector } PropertyEditor::~PropertyEditor() { diff --git a/editor/property_editor.h b/editor/property_editor.h index 56743822d2..7d8fa22f3f 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -64,7 +64,7 @@ protected: public: virtual String converts_to() const; virtual bool handles(const Ref<Resource> &p_resource) const; - virtual Ref<Resource> convert(const Ref<Resource> &p_resource); + virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const; }; class CustomPropertyEditor : public Popup { diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp index 26dd931321..d2101f1e00 100644 --- a/editor/quick_open.cpp +++ b/editor/quick_open.cpp @@ -195,7 +195,7 @@ Vector<Pair<String, Ref<Texture> > > EditorQuickOpen::_sort_fs(Vector<Pair<Strin Vector<float> scores; scores.resize(list.size()); for (int i = 0; i < list.size(); i++) - scores[i] = _path_cmp(search_text, list[i].first); + scores.write[i] = _path_cmp(search_text, list[i].first); while (list.size() > 0) { @@ -258,6 +258,9 @@ void EditorQuickOpen::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { connect("confirmed", this, "_confirmed"); + + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2ffaa0ca12..e1bb622ea4 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -164,7 +164,7 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node Ref<PackedScene> sdata = ResourceLoader::load(p_files[i]); if (!sdata.is_valid()) { current_option = -1; - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -174,7 +174,7 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instanced_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_files[i])); accept->popup_centered_minsize(); error = true; @@ -233,7 +233,7 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { Ref<PackedScene> sdata = ResourceLoader::load(p_file); if (!sdata.is_valid()) { - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error loading scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -241,7 +241,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instanced_scene) { - accept->get_ok()->set_text(TTR("Ugh")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(vformat(TTR("Error instancing scene from %s"), p_file)); accept->popup_centered_minsize(); return; @@ -349,21 +349,33 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; Ref<Script> existing = selected->get_script(); - if (existing.is_valid()) - editor->push_item(existing.ptr()); - else { - String path = selected->get_filename(); - if (path == "") { - String root_path = editor_data->get_edited_scene_root()->get_filename(); - if (root_path == "") { - path = "res://" + selected->get_name(); - } else { - path = root_path.get_base_dir() + "/" + selected->get_name(); + + String path = selected->get_filename(); + if (path == "") { + String root_path = editor_data->get_edited_scene_root()->get_filename(); + if (root_path == "") { + path = "res://" + selected->get_name(); + } else { + path = root_path.get_base_dir() + "/" + selected->get_name(); + } + } + + String inherits = selected->get_class(); + if (existing.is_valid()) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptLanguage *l = ScriptServer::get_language(i); + if (l->get_type() == existing->get_class()) { + if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { + String name = l->get_global_class_name(existing->get_path(), NULL); + inherits = editor->get_editor_data().script_class_get_base(name); + } else if (l->can_inherit_from_file()) { + inherits = "\"" + existing->get_path() + "\""; + } } } - script_create_dialog->config(selected->get_class(), path); - script_create_dialog->popup_centered(); } + script_create_dialog->config(inherits, path); + script_create_dialog->popup_centered(); } break; case TOOL_CLEAR_SCRIPT: { @@ -401,7 +413,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (scene_tree->get_selected() == edited_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -462,7 +474,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (editor_selection->is_selected(edited_scene)) { current_option = -1; - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -532,7 +544,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (editor_selection->is_selected(edited_scene)) { current_option = -1; - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on the tree root.")); accept->popup_centered_minsize(); break; @@ -619,7 +631,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *scene = editor_data->get_edited_scene_root(); if (!scene) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done without a scene.")); accept->popup_centered_minsize(); break; @@ -628,7 +640,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.size() != 1) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); break; @@ -637,14 +649,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *tocopy = selection.front()->get(); if (tocopy == scene) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can not perform with the root node.")); accept->popup_centered_minsize(); break; } if (tocopy != editor_data->get_edited_scene_root() && tocopy->get_filename() != "") { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation can't be done on instanced scenes.")); accept->popup_centered_minsize(); break; @@ -724,7 +736,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } } break; - case TOOL_SCENE_CLEAR_INSTANCING: { + case TOOL_SCENE_MAKE_LOCAL: { List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { @@ -736,7 +748,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; ERR_FAIL_COND(node->get_filename() == String()); - undo_redo->create_action(TTR("Discard Instancing")); + undo_redo->create_action(TTR("Make Local")); undo_redo->add_do_method(node, "set_filename", ""); undo_redo->add_undo_method(node, "set_filename", node->get_filename()); _node_replace_owner(node, node, root); @@ -791,7 +803,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { switch (p_tool) { case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break; case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break; - case TOOL_CREATE_USER_INTERFACE: new_node = memnew(Control); break; + case TOOL_CREATE_USER_INTERFACE: { + Control *node = memnew(Control); + node->set_anchors_and_margins_preset(PRESET_WIDE); //more useful for resizable UIs. + new_node = node; + + } break; } editor_data->get_undo_redo().create_action("New Scene Root"); @@ -845,7 +862,8 @@ void SceneTreeDock::_notification(int p_what) { button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons")); button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons")); - filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed"); @@ -893,7 +911,8 @@ void SceneTreeDock::_notification(int p_what) { button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons")); button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons")); - filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); } break; case NOTIFICATION_PROCESS: { @@ -1426,13 +1445,9 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); } - editor_data->get_undo_redo().add_do_method(editor, "push_item", p_script.operator->()); - editor_data->get_undo_redo().add_undo_method(editor, "push_item", (Script *)NULL); - - editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); - editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); - editor_data->get_undo_redo().commit_action(); + + editor->push_item(p_script.operator->()); } void SceneTreeDock::_delete_confirm() { @@ -1521,16 +1536,9 @@ void SceneTreeDock::_delete_confirm() { void SceneTreeDock::_update_script_button() { if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { - if (EditorNode::get_singleton()->get_editor_selection()->get_selection().front()->key()->get_script().is_null()) { - button_create_script->show(); - button_clear_script->hide(); - } else { - button_create_script->hide(); - button_clear_script->show(); - } + button_create_script->show(); } else { button_create_script->hide(); - button_clear_script->hide(); } } @@ -1731,7 +1739,7 @@ void SceneTreeDock::_new_scene_from(String p_file) { List<Node *> selection = editor_selection->get_selected_node_list(); if (selection.size() != 1) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("This operation requires a single selected node.")); accept->popup_centered_minsize(); return; @@ -1749,7 +1757,7 @@ void SceneTreeDock::_new_scene_from(String p_file) { memdelete(copy); if (err != OK) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Couldn't save new scene. Likely dependencies (instances) couldn't be satisfied.")); accept->popup_centered_minsize(); return; @@ -1761,14 +1769,14 @@ void SceneTreeDock::_new_scene_from(String p_file) { err = ResourceSaver::save(p_file, sdata, flg); if (err != OK) { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Error saving scene.")); accept->popup_centered_minsize(); return; } _replace_with_branch_scene(p_file, base); } else { - accept->get_ok()->set_text(TTR("I see...")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Error duplicating scene to save it.")); accept->popup_centered_minsize(); return; @@ -1984,6 +1992,8 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (selection.size() == 1) { + Node *selected = selection[0]; + subresources.clear(); menu_subresources->clear(); menu_subresources->set_size(Size2(1, 1)); @@ -1994,18 +2004,23 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Add", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/add_child_node"), TOOL_NEW); menu->add_icon_shortcut(get_icon("Instance", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/instance_scene"), TOOL_INSTANCE); menu->add_separator(); + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); - menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + Ref<Script> existing = selected->get_script(); + if (existing.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } menu->add_separator(); menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); } menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); - menu->add_separator(); - menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); - menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); - menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); - menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); - + if (scene_tree->get_selected() != edited_scene) { + menu->add_separator(); + menu->add_icon_shortcut(get_icon("MoveUp", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_up"), TOOL_MOVE_UP); + menu->add_icon_shortcut(get_icon("MoveDown", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/move_down"), TOOL_MOVE_DOWN); + menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); + menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); + } if (selection.size() == 1) { menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); @@ -2028,7 +2043,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { bool placeholder = selection[0]->get_scene_instance_load_placeholder(); menu->add_check_item(TTR("Editable Children"), TOOL_SCENE_EDITABLE_CHILDREN); menu->add_check_item(TTR("Load As Placeholder"), TOOL_SCENE_USE_PLACEHOLDER); - menu->add_item(TTR("Discard Instancing"), TOOL_SCENE_CLEAR_INSTANCING); + menu->add_item(TTR("Make Local"), TOOL_SCENE_MAKE_LOCAL); menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN); menu->set_item_checked(menu->get_item_idx_from_text(TTR("Editable Children")), editable); menu->set_item_checked(menu->get_item_idx_from_text(TTR("Load As Placeholder")), placeholder); @@ -2324,6 +2339,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel menu = memnew(PopupMenu); add_child(menu); menu->connect("id_pressed", this, "_tool_selected"); + menu->set_hide_on_window_lose_focus(true); menu_subresources = memnew(PopupMenu); menu_subresources->set_name("Sub-Resources"); menu_subresources->connect("id_pressed", this, "_tool_selected"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index fd74611bde..57f4759747 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -77,7 +77,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_BUTTON_MAX, TOOL_SCENE_EDITABLE_CHILDREN, TOOL_SCENE_USE_PLACEHOLDER, - TOOL_SCENE_CLEAR_INSTANCING, + TOOL_SCENE_MAKE_LOCAL, TOOL_SCENE_OPEN, TOOL_SCENE_CLEAR_INHERITANCE, TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM, diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 9ce0e973f7..e483fde4bc 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -658,7 +658,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da Vector<float> p; p.resize(arr.size()); for (int i = 0; i < arr.size(); i++) { - p[i] = arr[i]; + p.write[i] = arr[i]; if (i < perf_items.size()) { float v = p[i]; @@ -693,7 +693,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da perf_items[i]->set_text(1, vs); perf_items[i]->set_tooltip(1, tt); if (p[i] > perf_max[i]) - perf_max[i] = p[i]; + perf_max.write[i] = p[i]; } } perf_history.push_front(p); @@ -807,7 +807,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da item.signature = "categ::" + name + "::" + item.name; item.name = item.name.capitalize(); c.total_time += item.total; - c.items[i / 2] = item; + c.items.write[i / 2] = item; } metric.categories.push_back(c); } @@ -844,7 +844,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da item.calls = calls; item.self = self; item.total = total; - funcs.items[i] = item; + funcs.items.write[i] = item; } metric.categories.push_back(funcs); @@ -1193,7 +1193,7 @@ void ScriptEditorDebugger::start() { perf_history.clear(); for (int i = 0; i < Performance::MONITOR_MAX; i++) { - perf_max[i] = 0; + perf_max.write[i] = 0; } int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); @@ -2076,7 +2076,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { it->set_selectable(1, false); it->set_text(0, name.capitalize()); perf_items.push_back(it); - perf_max[i] = 0; + perf_max.write[i] = 0; } } diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index fe379703e5..4ebba73cb3 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -114,22 +114,6 @@ void EditorSettingsDialog::popup_edit_settings() { _focus_current_search_box(); } -void EditorSettingsDialog::_clear_search_box() { - - if (search_box->get_text() == "") - return; - - search_box->clear(); - inspector->get_inspector()->update_tree(); -} - -void EditorSettingsDialog::_clear_shortcut_search_box() { - if (shortcut_search_box->get_text() == "") - return; - - shortcut_search_box->clear(); -} - void EditorSettingsDialog::_filter_shortcuts(const String &p_filter) { shortcut_filter = p_filter; _update_shortcuts(); @@ -198,10 +182,10 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { void EditorSettingsDialog::_update_icons() { - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - shortcut_search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); - shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + shortcut_search_box->set_right_icon(get_icon("Search", "EditorIcons")); + shortcut_search_box->set_clear_button_enabled(true); restart_close_button->set_icon(get_icon("Close", "EditorIcons")); restart_container->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -411,8 +395,6 @@ void EditorSettingsDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_settings_save"), &EditorSettingsDialog::_settings_save); ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed); ClassDB::bind_method(D_METHOD("_settings_property_edited"), &EditorSettingsDialog::_settings_property_edited); - ClassDB::bind_method(D_METHOD("_clear_search_box"), &EditorSettingsDialog::_clear_search_box); - ClassDB::bind_method(D_METHOD("_clear_shortcut_search_box"), &EditorSettingsDialog::_clear_shortcut_search_box); ClassDB::bind_method(D_METHOD("_shortcut_button_pressed"), &EditorSettingsDialog::_shortcut_button_pressed); ClassDB::bind_method(D_METHOD("_filter_shortcuts"), &EditorSettingsDialog::_filter_shortcuts); ClassDB::bind_method(D_METHOD("_update_shortcuts"), &EditorSettingsDialog::_update_shortcuts); @@ -451,10 +433,6 @@ EditorSettingsDialog::EditorSettingsDialog() { search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->add_child(search_box); - clear_button = memnew(ToolButton); - hbc->add_child(clear_button); - clear_button->connect("pressed", this, "_clear_search_box"); - inspector = memnew(SectionedInspector); //inspector->hide_top_label(); inspector->get_inspector()->set_use_filter(true); @@ -500,10 +478,6 @@ EditorSettingsDialog::EditorSettingsDialog() { hbc->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", this, "_filter_shortcuts"); - shortcut_clear_button = memnew(ToolButton); - hbc->add_child(shortcut_clear_button); - shortcut_clear_button->connect("pressed", this, "_clear_shortcut_search_box"); - shortcuts = memnew(Tree); tab_shortcuts->add_child(shortcuts, true); shortcuts->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h index 6cf2eb6bdf..37d32e401d 100644 --- a/editor/settings_config_dialog.h +++ b/editor/settings_config_dialog.h @@ -52,8 +52,6 @@ class EditorSettingsDialog : public AcceptDialog { LineEdit *search_box; LineEdit *shortcut_search_box; - ToolButton *clear_button; - ToolButton *shortcut_clear_button; SectionedInspector *inspector; Timer *timer; diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index c45dea0df7..64fdc778d0 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -33,6 +33,7 @@ #include "geometry.h" #include "quick_hull.h" #include "scene/3d/camera.h" +#include "scene/3d/soft_body.h" #include "scene/resources/box_shape.h" #include "scene/resources/capsule_shape.h" #include "scene/resources/convex_polygon_shape.h" @@ -46,7 +47,10 @@ // Keep small children away from this file. // It's so ugly it will eat them alive -#define HANDLE_HALF_SIZE 0.05 +// The previous comment is kept only for historical reasons. +// No children will be harmed by the viewing of this file... hopefully. + +#define HANDLE_HALF_SIZE 9.5 bool EditorSpatialGizmo::can_draw() const { return is_editable(); @@ -84,11 +88,37 @@ void EditorSpatialGizmo::clear() { void EditorSpatialGizmo::redraw() { - if (get_script_instance() && get_script_instance()->has_method("redraw")) - get_script_instance()->call("redraw"); + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->redraw(this); +} + +String EditorSpatialGizmo::get_handle_name(int p_idx) const { + ERR_FAIL_COND_V(!gizmo_plugin, ""); + return gizmo_plugin->get_handle_name(this, p_idx); +} + +Variant EditorSpatialGizmo::get_handle_value(int p_idx) { + ERR_FAIL_COND_V(!gizmo_plugin, Variant()); + return gizmo_plugin->get_handle_value(this, p_idx); +} + +void EditorSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { + ERR_FAIL_COND(!gizmo_plugin); + return gizmo_plugin->set_handle(this, p_idx, p_camera, p_point); +} + +void EditorSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + ERR_FAIL_COND(!gizmo_plugin); + return gizmo_plugin->commit_handle(this, p_idx, p_restore, p_cancel); +} + +void EditorSpatialGizmo::set_spatial_node(Spatial *p_node) { + + ERR_FAIL_NULL(p_node); + spatial_node = p_node; } -void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base) { +void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base, bool p_hidden) { instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario()); VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); @@ -97,7 +127,8 @@ void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base) { if (extra_margin) VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF); - VS::get_singleton()->instance_set_layer_mask(instance, 1 << SpatialEditorViewport::GIZMO_EDIT_LAYER); //gizmos are 26 + int layer = p_hidden ? 0 : 1 << SpatialEditorViewport::GIZMO_EDIT_LAYER; + VS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26 } void EditorSpatialGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard, const RID &p_skeleton) { @@ -109,7 +140,7 @@ void EditorSpatialGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard ins.mesh = p_mesh; ins.skeleton = p_skeleton; if (valid) { - ins.create_instance(spatial_node); + ins.create_instance(spatial_node, hidden); VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); } @@ -158,7 +189,7 @@ void EditorSpatialGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Mat ins.billboard = p_billboard; ins.mesh = mesh; if (valid) { - ins.create_instance(spatial_node); + ins.create_instance(spatial_node, hidden); VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); } @@ -209,7 +240,7 @@ void EditorSpatialGizmo::add_unscaled_billboard(const Ref<Material> &p_material, ins.unscaled = true; ins.billboard = true; if (valid) { - ins.create_instance(spatial_node); + ins.create_instance(spatial_node, hidden); VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); } @@ -228,11 +259,11 @@ void EditorSpatialGizmo::add_collision_segments(const Vector<Vector3> &p_lines) collision_segments.resize(from + p_lines.size()); for (int i = 0; i < p_lines.size(); i++) { - collision_segments[from + i] = p_lines[i]; + collision_segments.write[from + i] = p_lines[i]; } } -void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_billboard, bool p_secondary) { +void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard, bool p_secondary) { billboard_handle = p_billboard; @@ -256,17 +287,18 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi for (int i = 0; i < p_handles.size(); i++) { Color col(1, 1, 1, 1); + if (gizmo_plugin->is_gizmo_handle_highlighted(this, i)) + col = Color(0, 0, 1, 0.9); + if (SpatialEditor::get_singleton()->get_over_gizmo_handle() != i) - col = Color(0.9, 0.9, 0.9, 0.9); + col.a = 0.8; + w[i] = col; } } a[VS::ARRAY_COLOR] = colors; mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); - if (p_billboard) - mesh->surface_set_material(0, SpatialEditorGizmos::singleton->handle2_material_billboard); - else - mesh->surface_set_material(0, SpatialEditorGizmos::singleton->handle2_material); + mesh->surface_set_material(0, p_material); if (p_billboard) { float md = 0; @@ -283,7 +315,7 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi ins.billboard = p_billboard; ins.extra_margin = true; if (valid) { - ins.create_instance(spatial_node); + ins.create_instance(spatial_node, hidden); VS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); } instances.push_back(ins); @@ -291,14 +323,14 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi int chs = handles.size(); handles.resize(chs + p_handles.size()); for (int i = 0; i < p_handles.size(); i++) { - handles[i + chs] = p_handles[i]; + handles.write[i + chs] = p_handles[i]; } } else { int chs = secondary_handles.size(); secondary_handles.resize(chs + p_handles.size()); for (int i = 0; i < p_handles.size(); i++) { - secondary_handles[i + chs] = p_handles[i]; + secondary_handles.write[i + chs] = p_handles[i]; } } } @@ -325,17 +357,13 @@ void EditorSpatialGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size add_mesh(m); } -void EditorSpatialGizmo::set_spatial_node(Spatial *p_node) { - - ERR_FAIL_NULL(p_node); - spatial_node = p_node; -} - bool EditorSpatialGizmo::intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum) { ERR_FAIL_COND_V(!spatial_node, false); ERR_FAIL_COND_V(!valid, false); + if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; + if (selectable_icon_size > 0.0f) { Vector3 origin = spatial_node->get_global_transform().get_origin(); @@ -408,7 +436,9 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, ERR_FAIL_COND_V(!spatial_node, false); ERR_FAIL_COND_V(!valid, false); - if (r_gizmo_handle) { + if (hidden && !gizmo_plugin->is_selectable_when_hidden()) return false; + + if (r_gizmo_handle && !hidden) { Transform t = spatial_node->get_global_transform(); t.orthonormalize(); @@ -423,7 +453,8 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 hpos = t.xform(secondary_handles[i]); Vector2 p = p_camera->unproject_position(hpos); - if (p.distance_to(p_point) < SpatialEditorGizmos::singleton->handle_t->get_width() * 0.6) { + + if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { real_t dp = p_camera->get_transform().origin.distance_to(hpos); if (dp < min_d) { @@ -448,7 +479,8 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 hpos = t.xform(handles[i]); Vector2 p = p_camera->unproject_position(hpos); - if (p.distance_to(p_point) < SpatialEditorGizmos::singleton->handle_t->get_width() * 0.6) { + + if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { real_t dp = p_camera->get_transform().origin.distance_to(hpos); if (dp < min_d) { @@ -499,6 +531,8 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, rect.set_position(center - rect.get_size() / 2.0); if (rect.has_point(p_point)) { + r_pos = t.origin; + r_normal = -p_camera->project_ray_normal(p_point); return true; } @@ -592,7 +626,7 @@ void EditorSpatialGizmo::create() { for (int i = 0; i < instances.size(); i++) { - instances[i].create_instance(spatial_node); + instances.write[i].create_instance(spatial_node, hidden); } transform(); @@ -616,99 +650,24 @@ void EditorSpatialGizmo::free() { if (instances[i].instance.is_valid()) VS::get_singleton()->free(instances[i].instance); - instances[i].instance = RID(); + instances.write[i].instance = RID(); } + clear(); + valid = false; } -Ref<SpatialMaterial> EditorSpatialGizmo::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { - - String name = p_name; - - if (!is_editable()) { - name += "@readonly"; - } else if (is_selected()) { - name += "@selected"; - } - - if (SpatialEditorGizmos::singleton->material_cache.has(name)) { - return SpatialEditorGizmos::singleton->material_cache[name]; - } - - Color color = p_color; - - if (!is_editable()) { - color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instanced"); +void EditorSpatialGizmo::set_hidden(bool p_hidden) { + hidden = p_hidden; + int layer = hidden ? 0 : 1 << SpatialEditorViewport::GIZMO_EDIT_LAYER; + for (int i = 0; i < instances.size(); ++i) { + VS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer); } - if (!is_selected()) { - color.a *= 0.3; - } - - Ref<SpatialMaterial> line_material; - line_material.instance(); - line_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - line_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - if (p_use_vertex_color) { - line_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - } - - if (p_billboard) { - line_material->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - } - - if (p_on_top && is_selected()) { - line_material->set_on_top_of_alpha(); - } - - line_material->set_albedo(color); - - SpatialEditorGizmos::singleton->material_cache[name] = line_material; - - return line_material; } -Ref<SpatialMaterial> EditorSpatialGizmo::create_icon_material(const String &p_name, const Ref<Texture> &p_texture, bool p_on_top, const Color &p_albedo) { - - String name = p_name; - - if (!is_editable()) { - name += "@readonly"; - } else if (is_selected()) { - name += "@selected"; - } - - if (SpatialEditorGizmos::singleton->material_cache.has(name)) { - return SpatialEditorGizmos::singleton->material_cache[name]; - } - - Color color = p_albedo; - - if (!is_editable()) { - color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instanced"); - } else if (!is_selected()) { - color.a *= 0.3; - } - - Ref<SpatialMaterial> icon; - icon.instance(); - icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - icon->set_albedo(color); - icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, p_texture); - icon->set_flag(SpatialMaterial::FLAG_FIXED_SIZE, true); - icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - - if (p_on_top && is_selected()) { - icon->set_on_top_of_alpha(); - } - - SpatialEditorGizmos::singleton->material_cache[name] = icon; - - return icon; +void EditorSpatialGizmo::set_plugin(EditorSpatialGizmoPlugin *p_plugin) { + gizmo_plugin = p_plugin; } void EditorSpatialGizmo::_bind_methods() { @@ -721,6 +680,7 @@ void EditorSpatialGizmo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_handles", "handles", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorSpatialGizmo::_set_spatial_node); ClassDB::bind_method(D_METHOD("clear"), &EditorSpatialGizmo::clear); + ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorSpatialGizmo::set_hidden); BIND_VMETHOD(MethodInfo("redraw")); BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", PropertyInfo(Variant::INT, "index"))); @@ -738,12 +698,17 @@ void EditorSpatialGizmo::_bind_methods() { EditorSpatialGizmo::EditorSpatialGizmo() { valid = false; billboard_handle = false; + hidden = false; base = NULL; + selected = false; + instanced = false; spatial_node = NULL; + gizmo_plugin = NULL; } EditorSpatialGizmo::~EditorSpatialGizmo() { + if (gizmo_plugin != NULL) gizmo_plugin->unregister_gizmo(this); clear(); } @@ -756,7 +721,30 @@ Vector3 EditorSpatialGizmo::get_handle_pos(int p_idx) const { //// light gizmo -String LightSpatialGizmo::get_handle_name(int p_idx) const { +LightSpatialGizmoPlugin::LightSpatialGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/light", Color(1, 1, 0.2)); + + create_material("lines", gizmo_color); + create_material("lines_billboard", gizmo_color, true); + + create_icon_material("light_directional_icon", SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight", "EditorIcons")); + create_icon_material("light_omni_icon", SpatialEditor::get_singleton()->get_icon("GizmoLight", "EditorIcons")); + create_icon_material("light_spot_icon", SpatialEditor::get_singleton()->get_icon("GizmoSpotLight", "EditorIcons")); + + create_handle_material("handles"); + create_handle_material("handles_billboard", true); +} + +bool LightSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Light>(p_spatial) != NULL; +} + +String LightSpatialGizmoPlugin::get_name() const { + return "Lights"; +} + +String LightSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { if (p_idx == 0) return "Radius"; @@ -764,8 +752,9 @@ String LightSpatialGizmo::get_handle_name(int p_idx) const { return "Aperture"; } -Variant LightSpatialGizmo::get_handle_value(int p_idx) const { +Variant LightSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + Light *light = Object::cast_to<Light>(p_gizmo->get_spatial_node()); if (p_idx == 0) return light->get_param(Light::PARAM_RANGE); if (p_idx == 1) @@ -803,8 +792,9 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec return a * 180.0 / Math_PI; } -void LightSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void LightSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + Light *light = Object::cast_to<Light>(p_gizmo->get_spatial_node()); Transform gt = light->get_global_transform(); gt.orthonormalize(); Transform gi = gt.affine_inverse(); @@ -843,8 +833,9 @@ void LightSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_ } } -void LightSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void LightSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + Light *light = Object::cast_to<Light>(p_gizmo->get_spatial_node()); if (p_cancel) { light->set_param(p_idx == 0 ? Light::PARAM_RANGE : Light::PARAM_SPOT_ANGLE, p_restore); @@ -866,14 +857,16 @@ void LightSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool } } -void LightSpatialGizmo::redraw() { +void LightSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + Light *light = Object::cast_to<Light>(p_gizmo->get_spatial_node()); - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/light"); + p_gizmo->clear(); if (Object::cast_to<DirectionalLight>(light)) { - Ref<Material> material = create_material("light_directional_material", gizmo_color); - Ref<Material> icon = create_icon_material("light_directional_icon", SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight", "EditorIcons")); + Ref<Material> material = get_material("lines", p_gizmo); + Ref<Material> icon = get_material("light_directional_icon", p_gizmo); const int arrow_points = 7; const float arrow_length = 1.5; @@ -904,16 +897,15 @@ void LightSpatialGizmo::redraw() { } } - add_lines(lines, material); - add_collision_segments(lines); - add_unscaled_billboard(icon, 0.05); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_unscaled_billboard(icon, 0.05); } if (Object::cast_to<OmniLight>(light)) { - Ref<Material> material = create_material("light_omni_material", gizmo_color, true); - Ref<Material> icon = create_icon_material("light_omni_icon", SpatialEditor::get_singleton()->get_icon("GizmoLight", "EditorIcons")); - clear(); + Ref<Material> material = get_material("lines_billboard", p_gizmo); + Ref<Material> icon = get_material("light_omni_icon", p_gizmo); OmniLight *on = Object::cast_to<OmniLight>(light); @@ -936,29 +928,27 @@ void LightSpatialGizmo::redraw() { points.push_back(Vector3(b.x, b.y, 0)); } - add_lines(points, material, true); - add_collision_segments(points); + p_gizmo->add_lines(points, material, true); + p_gizmo->add_collision_segments(points); - add_unscaled_billboard(icon, 0.05); + p_gizmo->add_unscaled_billboard(icon, 0.05); Vector<Vector3> handles; handles.push_back(Vector3(r, 0, 0)); - add_handles(handles, true); + p_gizmo->add_handles(handles, get_material("handles_billboard"), true); } if (Object::cast_to<SpotLight>(light)) { - Ref<Material> material = create_material("light_spot_material", gizmo_color); - Ref<Material> icon = create_icon_material("light_spot_icon", SpatialEditor::get_singleton()->get_icon("GizmoSpotLight", "EditorIcons")); - - clear(); + Ref<Material> material = get_material("lines", p_gizmo); + Ref<Material> icon = get_material("light_spot_icon", p_gizmo); Vector<Vector3> points; - SpotLight *on = Object::cast_to<SpotLight>(light); + SpotLight *sl = Object::cast_to<SpotLight>(light); - float r = on->get_param(Light::PARAM_RANGE); - float w = r * Math::sin(Math::deg2rad(on->get_param(Light::PARAM_SPOT_ANGLE))); - float d = r * Math::cos(Math::deg2rad(on->get_param(Light::PARAM_SPOT_ANGLE))); + float r = sl->get_param(Light::PARAM_RANGE); + float w = r * Math::sin(Math::deg2rad(sl->get_param(Light::PARAM_SPOT_ANGLE))); + float d = r * Math::cos(Math::deg2rad(sl->get_param(Light::PARAM_SPOT_ANGLE))); for (int i = 0; i < 360; i++) { @@ -980,7 +970,7 @@ void LightSpatialGizmo::redraw() { points.push_back(Vector3(0, 0, -r)); points.push_back(Vector3()); - add_lines(points, material); + p_gizmo->add_lines(points, material); Vector<Vector3> handles; handles.push_back(Vector3(0, 0, -r)); @@ -1012,33 +1002,45 @@ void LightSpatialGizmo::redraw() { collision_segments.push_back(Vector3(0, 0, -r)); collision_segments.push_back(Vector3()); - add_handles(handles); - add_collision_segments(collision_segments); - add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_collision_segments(collision_segments); + p_gizmo->add_unscaled_billboard(icon, 0.05); } } -LightSpatialGizmo::LightSpatialGizmo(Light *p_light) { +////// - light = p_light; - set_spatial_node(p_light); +//// player gizmo +AudioStreamPlayer3DSpatialGizmoPlugin::AudioStreamPlayer3DSpatialGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); + + create_icon_material("stream_player_3d_icon", SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer", "EditorIcons")); + create_material("stream_player_3d_material", gizmo_color); + create_handle_material("handles"); } -////// +bool AudioStreamPlayer3DSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<AudioStreamPlayer3D>(p_spatial) != NULL; +} -//// player gizmo +String AudioStreamPlayer3DSpatialGizmoPlugin::get_name() const { + return "AudioStreamPlayer3D"; +} -String AudioStreamPlayer3DSpatialGizmo::get_handle_name(int p_idx) const { +String AudioStreamPlayer3DSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { return "Emission Radius"; } -Variant AudioStreamPlayer3DSpatialGizmo::get_handle_value(int p_idx) const { - +Variant AudioStreamPlayer3DSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); return player->get_emission_angle(); } -void AudioStreamPlayer3DSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void AudioStreamPlayer3DSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); Transform gt = player->get_global_transform(); gt.orthonormalize(); @@ -1076,7 +1078,9 @@ void AudioStreamPlayer3DSpatialGizmo::set_handle(int p_idx, Camera *p_camera, co } } -void AudioStreamPlayer3DSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void AudioStreamPlayer3DSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); if (p_cancel) { @@ -1092,16 +1096,17 @@ void AudioStreamPlayer3DSpatialGizmo::commit_handle(int p_idx, const Variant &p_ } } -void AudioStreamPlayer3DSpatialGizmo::redraw() { +void AudioStreamPlayer3DSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node()); - Ref<Material> icon = create_icon_material("stream_player_3d_material", SpatialEditor::get_singleton()->get_icon("GizmoSpatialSamplePlayer", "EditorIcons")); + p_gizmo->clear(); + + Ref<Material> icon = get_material("stream_player_3d_icon", p_gizmo); if (player->is_emission_angle_enabled()) { - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/stream_player_3d"); - Ref<Material> material = create_material("stream_player_3d_material", gizmo_color); + Ref<Material> material = get_material("stream_player_3d_material", p_gizmo); float pc = player->get_emission_angle(); @@ -1119,8 +1124,8 @@ void AudioStreamPlayer3DSpatialGizmo::redraw() { Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs); - points[i * 2 + 0] = from; - points[i * 2 + 1] = to; + points.write[i * 2 + 0] = from; + points.write[i * 2 + 1] = to; } for (int i = 0; i < 4; i++) { @@ -1129,31 +1134,44 @@ void AudioStreamPlayer3DSpatialGizmo::redraw() { Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); - points[200 + i * 2 + 0] = from; - points[200 + i * 2 + 1] = Vector3(); + points.write[200 + i * 2 + 0] = from; + points.write[200 + i * 2 + 1] = Vector3(); } - add_lines(points, material); - add_collision_segments(points); + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); Vector<Vector3> handles; float ha = Math::deg2rad(player->get_emission_angle()); handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); - add_handles(handles); + p_gizmo->add_handles(handles, get_material("handles")); } - add_unscaled_billboard(icon, 0.05); + p_gizmo->add_unscaled_billboard(icon, 0.05); } -AudioStreamPlayer3DSpatialGizmo::AudioStreamPlayer3DSpatialGizmo(AudioStreamPlayer3D *p_player) { +////// + +CameraSpatialGizmoPlugin::CameraSpatialGizmoPlugin() { - player = p_player; - set_spatial_node(p_player); + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); + + create_material("camera_material", gizmo_color); + create_icon_material("camera_icon", SpatialEditor::get_singleton()->get_icon("GizmoCamera", "EditorIcons")); + create_handle_material("handles"); } -////// +bool CameraSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Camera>(p_spatial) != NULL; +} + +String CameraSpatialGizmoPlugin::get_name() const { + return "Camera"; +} + +String CameraSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { -String CameraSpatialGizmo::get_handle_name(int p_idx) const { + Camera *camera = Object::cast_to<Camera>(p_gizmo->get_spatial_node()); if (camera->get_projection() == Camera::PROJECTION_PERSPECTIVE) { return "FOV"; @@ -1161,7 +1179,10 @@ String CameraSpatialGizmo::get_handle_name(int p_idx) const { return "Size"; } } -Variant CameraSpatialGizmo::get_handle_value(int p_idx) const { + +Variant CameraSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + + Camera *camera = Object::cast_to<Camera>(p_gizmo->get_spatial_node()); if (camera->get_projection() == Camera::PROJECTION_PERSPECTIVE) { return camera->get_fov(); @@ -1170,7 +1191,10 @@ Variant CameraSpatialGizmo::get_handle_value(int p_idx) const { return camera->get_size(); } } -void CameraSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { + +void CameraSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + Camera *camera = Object::cast_to<Camera>(p_gizmo->get_spatial_node()); Transform gt = camera->get_global_transform(); gt.orthonormalize(); @@ -1196,7 +1220,10 @@ void CameraSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p camera->set("size", d); } } -void CameraSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + +void CameraSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + Camera *camera = Object::cast_to<Camera>(p_gizmo->get_spatial_node()); if (camera->get_projection() == Camera::PROJECTION_PERSPECTIVE) { @@ -1226,16 +1253,17 @@ void CameraSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool } } -void CameraSpatialGizmo::redraw() { +void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + Camera *camera = Object::cast_to<Camera>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Vector<Vector3> lines; Vector<Vector3> handles; - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/camera"); - Ref<Material> material = create_material("camera_material", gizmo_color); - Ref<Material> icon = create_icon_material("camera_icon", SpatialEditor::get_singleton()->get_icon("GizmoCamera", "EditorIcons")); + Ref<Material> material = get_material("camera_material", p_gizmo); + Ref<Material> icon = get_material("camera_icon", p_gizmo); switch (camera->get_projection()) { @@ -1305,71 +1333,81 @@ void CameraSpatialGizmo::redraw() { } break; } - add_lines(lines, material); - add_collision_segments(lines); - add_unscaled_billboard(icon, 0.05); - add_handles(handles); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); } -CameraSpatialGizmo::CameraSpatialGizmo(Camera *p_camera) { +////// - camera = p_camera; - set_spatial_node(camera); +MeshInstanceSpatialGizmoPlugin::MeshInstanceSpatialGizmoPlugin() { } -////// +bool MeshInstanceSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<MeshInstance>(p_spatial) != NULL && Object::cast_to<SoftBody>(p_spatial) == NULL; +} -bool MeshInstanceSpatialGizmo::can_draw() const { - return true; //mesh can always draw (even though nothing is displayed) +String MeshInstanceSpatialGizmoPlugin::get_name() const { + return "MeshInstance"; } -void MeshInstanceSpatialGizmo::redraw() { - clear(); +bool MeshInstanceSpatialGizmoPlugin::can_be_hidden() const { + return false; +} + +void MeshInstanceSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + MeshInstance *mesh = Object::cast_to<MeshInstance>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Ref<Mesh> m = mesh->get_mesh(); + if (!m.is_valid()) return; //none Ref<TriangleMesh> tm = m->generate_triangle_mesh(); if (tm.is_valid()) { - add_collision_triangles(tm); + p_gizmo->add_collision_triangles(tm); } } -MeshInstanceSpatialGizmo::MeshInstanceSpatialGizmo(MeshInstance *p_mesh) { +///// +Sprite3DSpatialGizmoPlugin::Sprite3DSpatialGizmoPlugin() { +} - mesh = p_mesh; - set_spatial_node(p_mesh); +bool Sprite3DSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Sprite3D>(p_spatial) != NULL; } -///// +String Sprite3DSpatialGizmoPlugin::get_name() const { + return "Sprite3D"; +} -bool Sprite3DSpatialGizmo::can_draw() const { - return true; +bool Sprite3DSpatialGizmoPlugin::can_be_hidden() const { + return false; } -void Sprite3DSpatialGizmo::redraw() { - clear(); +void Sprite3DSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + Sprite3D *sprite = Object::cast_to<Sprite3D>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Ref<TriangleMesh> tm = sprite->generate_triangle_mesh(); if (tm.is_valid()) { - add_collision_triangles(tm); + p_gizmo->add_collision_triangles(tm); } } -Sprite3DSpatialGizmo::Sprite3DSpatialGizmo(SpriteBase3D *p_sprite) { - - sprite = p_sprite; - set_spatial_node(p_sprite); -} - /// -void Position3DSpatialGizmo::redraw() { +Position3DSpatialGizmoPlugin::Position3DSpatialGizmoPlugin() { + pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + cursor_points = Vector<Vector3>(); - clear(); - add_mesh(SpatialEditorGizmos::singleton->pos3d_mesh); - Vector<Vector3> cursor_points; + PoolVector<Color> cursor_colors; float cs = 0.25; cursor_points.push_back(Vector3(+cs, 0, 0)); cursor_points.push_back(Vector3(-cs, 0, 0)); @@ -1377,51 +1415,65 @@ void Position3DSpatialGizmo::redraw() { cursor_points.push_back(Vector3(0, -cs, 0)); cursor_points.push_back(Vector3(0, 0, +cs)); cursor_points.push_back(Vector3(0, 0, -cs)); - add_collision_segments(cursor_points); + cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); + cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); + cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); + cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); + cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); + cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); + + Ref<SpatialMaterial> mat = memnew(SpatialMaterial); + mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); + mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); + mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + mat->set_line_width(3); + Array d; + d.resize(VS::ARRAY_MAX); + d[Mesh::ARRAY_VERTEX] = cursor_points; + d[Mesh::ARRAY_COLOR] = cursor_colors; + pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); + pos3d_mesh->surface_set_material(0, mat); } -Position3DSpatialGizmo::Position3DSpatialGizmo(Position3D *p_p3d) { +bool Position3DSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Position3D>(p_spatial) != NULL; +} - p3d = p_p3d; - set_spatial_node(p3d); +String Position3DSpatialGizmoPlugin::get_name() const { + return "Position3D"; +} + +void Position3DSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + p_gizmo->clear(); + p_gizmo->add_mesh(pos3d_mesh); + p_gizmo->add_collision_segments(cursor_points); } ///// -void SkeletonSpatialGizmo::redraw() { +SkeletonSpatialGizmoPlugin::SkeletonSpatialGizmoPlugin() { - clear(); + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); + create_material("skeleton_material", gizmo_color); +} - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/skeleton"); - Ref<Material> material = create_material("skeleton_material", gizmo_color); - SpatialMaterial *sm = Object::cast_to<SpatialMaterial>(material.ptr()); - - { // Reset - Color c(sm->get_albedo()); - c.a = 1; - sm->set_albedo(c); - } - if (sm) { - switch (SpatialEditor::get_singleton()->get_skeleton_visibility_state()) { - case 0: { - // Hidden - Color c(sm->get_albedo()); - c.a = 0; - sm->set_albedo(c); - sm->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - } break; - case 1: - // Visible - sm->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, false); - sm->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN); - sm->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, false); - break; - case 2: - // x-ray - sm->set_on_top_of_alpha(); - break; - } - } +bool SkeletonSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Skeleton>(p_spatial) != NULL; +} + +String SkeletonSpatialGizmoPlugin::get_name() const { + return "Skeleton"; +} + +void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + Skeleton *skel = Object::cast_to<Skeleton>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref<Material> material = get_material("skeleton_material", p_gizmo); Ref<SurfaceTool> surface_tool(memnew(SurfaceTool)); @@ -1436,23 +1488,25 @@ void SkeletonSpatialGizmo::redraw() { weights.resize(4); for (int i = 0; i < 4; i++) { - bones[i] = 0; - weights[i] = 0; + bones.write[i] = 0; + weights.write[i] = 0; } - weights[0] = 1; + weights.write[0] = 1; AABB aabb; Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); - for (int i = 0; i < skel->get_bone_count(); i++) { + for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) { + + int i = skel->get_process_order(i_bone); int parent = skel->get_bone_parent(i); if (parent >= 0) { - grests[i] = grests[parent] * skel->get_bone_rest(i); + grests.write[i] = grests[parent] * skel->get_bone_rest(i); Vector3 v0 = grests[parent].origin; Vector3 v1 = grests[i].origin; @@ -1475,7 +1529,7 @@ void SkeletonSpatialGizmo::redraw() { int pointidx = 0; for (int j = 0; j < 3; j++) { - bones[0] = parent; + bones.write[0] = parent; surface_tool->add_bones(bones); surface_tool->add_weights(weights); surface_tool->add_color(rootcolor); @@ -1503,7 +1557,7 @@ void SkeletonSpatialGizmo::redraw() { Vector3 point = v0 + d * dist * 0.2; point += axis * dist * 0.1; - bones[0] = parent; + bones.write[0] = parent; surface_tool->add_bones(bones); surface_tool->add_weights(weights); surface_tool->add_color(bonecolor); @@ -1513,12 +1567,12 @@ void SkeletonSpatialGizmo::redraw() { surface_tool->add_color(bonecolor); surface_tool->add_vertex(point); - bones[0] = parent; + bones.write[0] = parent; surface_tool->add_bones(bones); surface_tool->add_weights(weights); surface_tool->add_color(bonecolor); surface_tool->add_vertex(point); - bones[0] = i; + bones.write[0] = i; surface_tool->add_bones(bones); surface_tool->add_weights(weights); surface_tool->add_color(bonecolor); @@ -1530,7 +1584,7 @@ void SkeletonSpatialGizmo::redraw() { SWAP(points[1], points[2]); for (int j = 0; j < 4; j++) { - bones[0] = parent; + bones.write[0] = parent; surface_tool->add_bones(bones); surface_tool->add_weights(weights); surface_tool->add_color(bonecolor); @@ -1555,8 +1609,8 @@ void SkeletonSpatialGizmo::redraw() { */ } else { - grests[i] = skel->get_bone_rest(i); - bones[0] = i; + grests.write[i] = skel->get_bone_rest(i); + bones.write[0] = i; } /* Transform t = grests[i]; @@ -1599,24 +1653,28 @@ void SkeletonSpatialGizmo::redraw() { } Ref<ArrayMesh> m = surface_tool->commit(); - add_mesh(m, false, skel->get_skeleton()); + p_gizmo->add_mesh(m, false, skel->get_skeleton()); } -SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton *p_skel) { +//// + +PhysicalBoneSpatialGizmoPlugin::PhysicalBoneSpatialGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); +} - skel = p_skel; - set_spatial_node(p_skel); +bool PhysicalBoneSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<PhysicalBone>(p_spatial) != NULL; } -PhysicalBoneSpatialGizmo::PhysicalBoneSpatialGizmo(PhysicalBone *p_pb) : - EditorSpatialGizmo(), - physical_bone(p_pb) { - set_spatial_node(p_pb); +String PhysicalBoneSpatialGizmoPlugin::get_name() const { + return "PhysicalBones"; } -void PhysicalBoneSpatialGizmo::redraw() { +void PhysicalBoneSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + p_gizmo->clear(); + + PhysicalBone *physical_bone = Object::cast_to<PhysicalBone>(p_gizmo->get_spatial_node()); if (!physical_bone) return; @@ -1630,26 +1688,25 @@ void PhysicalBoneSpatialGizmo::redraw() { switch (physical_bone->get_joint_type()) { case PhysicalBone::JOINT_TYPE_PIN: { - PinJointSpatialGizmo::CreateGizmo(physical_bone->get_joint_offset(), points); + JointSpatialGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points); } break; case PhysicalBone::JOINT_TYPE_CONE: { const PhysicalBone::ConeJointData *cjd(static_cast<const PhysicalBone::ConeJointData *>(physical_bone->get_joint_data())); - ConeTwistJointSpatialGizmo::CreateGizmo( + JointSpatialGizmoPlugin::CreateConeTwistJointGizmo( physical_bone->get_joint_offset(), physical_bone->get_global_transform() * physical_bone->get_joint_offset(), pb ? pb->get_global_transform() : Transform(), pbp ? pbp->get_global_transform() : Transform(), cjd->swing_span, cjd->twist_span, - points, pb ? &points : NULL, pbp ? &points : NULL); } break; case PhysicalBone::JOINT_TYPE_HINGE: { const PhysicalBone::HingeJointData *hjd(static_cast<const PhysicalBone::HingeJointData *>(physical_bone->get_joint_data())); - HingeJointSpatialGizmo::CreateGizmo( + JointSpatialGizmoPlugin::CreateHingeJointGizmo( physical_bone->get_joint_offset(), physical_bone->get_global_transform() * physical_bone->get_joint_offset(), pb ? pb->get_global_transform() : Transform(), @@ -1664,7 +1721,7 @@ void PhysicalBoneSpatialGizmo::redraw() { case PhysicalBone::JOINT_TYPE_SLIDER: { const PhysicalBone::SliderJointData *sjd(static_cast<const PhysicalBone::SliderJointData *>(physical_bone->get_joint_data())); - SliderJointSpatialGizmo::CreateGizmo( + JointSpatialGizmoPlugin::CreateSliderJointGizmo( physical_bone->get_joint_offset(), physical_bone->get_global_transform() * physical_bone->get_joint_offset(), pb ? pb->get_global_transform() : Transform(), @@ -1680,7 +1737,7 @@ void PhysicalBoneSpatialGizmo::redraw() { case PhysicalBone::JOINT_TYPE_6DOF: { const PhysicalBone::SixDOFJointData *sdofjd(static_cast<const PhysicalBone::SixDOFJointData *>(physical_bone->get_joint_data())); - Generic6DOFJointSpatialGizmo::CreateGizmo( + JointSpatialGizmoPlugin::CreateGeneric6DOFJointGizmo( physical_bone->get_joint_offset(), physical_bone->get_global_transform() * physical_bone->get_joint_offset(), @@ -1716,14 +1773,15 @@ void PhysicalBoneSpatialGizmo::redraw() { return; } - Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); + Ref<Material> material = get_material("joint_material", p_gizmo); - add_collision_segments(points); - add_lines(points, material); + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, material); } // FIXME: Kept as reference for reimplementation in 3.1+ #if 0 + void RoomSpatialGizmo::redraw() { clear(); @@ -1740,41 +1798,41 @@ void RoomSpatialGizmo::redraw() { for (int i = 0; i < fc; i++) { - Vector3 fn = r[i].get_plane().normal; + Vector3 fn = r[i].get_plane().normal; - for (int j = 0; j < 3; j++) { + for (int j = 0; j < 3; j++) { - _EdgeKey ek; - ek.from = r[i].vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - ek.to = r[i].vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - if (ek.from < ek.to) - SWAP(ek.from, ek.to); + _EdgeKey ek; + ek.from = r[i].vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + ek.to = r[i].vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + if (ek.from < ek.to) + SWAP(ek.from, ek.to); - Map<_EdgeKey, Vector3>::Element *E = edge_map.find(ek); + Map<_EdgeKey, Vector3>::Element *E = edge_map.find(ek); - if (E) { + if (E) { - if (E->get().dot(fn) > 0.9) { + if (E->get().dot(fn) > 0.9) { - E->get() = Vector3(); - } + E->get() = Vector3(); + } - } else { + } else { - edge_map[ek] = fn; - } + edge_map[ek] = fn; + } + } } - } for (Map<_EdgeKey, Vector3>::Element *E = edge_map.front(); E; E = E->next()) { - if (E->get() != Vector3()) { - lines.push_back(E->key().from); - lines.push_back(E->key().to); + if (E->get() != Vector3()) { + lines.push_back(E->key().from); + lines.push_back(E->key().to); + } } - } - add_lines(lines, SpatialEditorGizmos::singleton->room_material); + add_lines(lines, EditorSpatialGizmos::singleton->room_material); add_collision_segments(lines); } @@ -1792,31 +1850,31 @@ void PortalSpatialGizmo::redraw() { Vector<Point2> points = portal->get_shape(); if (points.size() == 0) { - return; - } + return; + } Vector<Vector3> lines; Vector3 center; for (int i = 0; i < points.size(); i++) { - Vector3 f; - f.x = points[i].x; - f.y = points[i].y; - Vector3 fn; - fn.x = points[(i + 1) % points.size()].x; - fn.y = points[(i + 1) % points.size()].y; - center += f; + Vector3 f; + f.x = points[i].x; + f.y = points[i].y; + Vector3 fn; + fn.x = points[(i + 1) % points.size()].x; + fn.y = points[(i + 1) % points.size()].y; + center += f; - lines.push_back(f); - lines.push_back(fn); - } + lines.push_back(f); + lines.push_back(fn); + } center /= points.size(); lines.push_back(center); lines.push_back(center + Vector3(0, 0, 1)); - add_lines(lines, SpatialEditorGizmos::singleton->portal_material); + add_lines(lines, EditorSpatialGizmos::singleton->portal_material); add_collision_segments(lines); } @@ -1829,33 +1887,58 @@ PortalSpatialGizmo::PortalSpatialGizmo(Portal *p_portal) { #endif ///// -void RayCastSpatialGizmo::redraw() { +RayCastSpatialGizmoPlugin::RayCastSpatialGizmoPlugin() { - clear(); + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool RayCastSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<RayCast>(p_spatial) != NULL; +} + +String RayCastSpatialGizmoPlugin::get_name() const { + return "RayCast"; +} + +void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + RayCast *raycast = Object::cast_to<RayCast>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Vector<Vector3> lines; lines.push_back(Vector3()); lines.push_back(raycast->get_cast_to()); - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape"); - Ref<Material> material = create_material("shape_material", gizmo_color); + Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo); - add_lines(lines, material); - add_collision_segments(lines); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); } -RayCastSpatialGizmo::RayCastSpatialGizmo(RayCast *p_raycast) { +///// + +VehicleWheelSpatialGizmoPlugin::VehicleWheelSpatialGizmoPlugin() { - set_spatial_node(p_raycast); - raycast = p_raycast; + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); } -///// +bool VehicleWheelSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<VehicleWheel>(p_spatial) != NULL; +} -void VehicleWheelSpatialGizmo::redraw() { +String VehicleWheelSpatialGizmoPlugin::get_name() const { + return "VehicleWheel"; +} - clear(); +void VehicleWheelSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + VehicleWheel *car_wheel = Object::cast_to<VehicleWheel>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Vector<Vector3> points; @@ -1899,657 +1982,170 @@ void VehicleWheelSpatialGizmo::redraw() { points.push_back(Vector3(0, -r, r * 2)); points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8)); - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape"); - Ref<Material> material = create_material("shape_material", gizmo_color); - - add_lines(points, material); - add_collision_segments(points); -} - -VehicleWheelSpatialGizmo::VehicleWheelSpatialGizmo(VehicleWheel *p_car_wheel) { + Ref<Material> material = get_material("shape_material", p_gizmo); - set_spatial_node(p_car_wheel); - car_wheel = p_car_wheel; + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); } /////////// -String CollisionShapeSpatialGizmo::get_handle_name(int p_idx) const { - - Ref<Shape> s = cs->get_shape(); - if (s.is_null()) - return ""; - - if (Object::cast_to<SphereShape>(*s)) { - - return "Radius"; - } - - if (Object::cast_to<BoxShape>(*s)) { - - return "Extents"; - } - - if (Object::cast_to<CapsuleShape>(*s)) { - - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to<CylinderShape>(*s)) { - - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to<RayShape>(*s)) { - - return "Length"; - } - - return ""; +SoftBodySpatialGizmoPlugin::SoftBodySpatialGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + create_handle_material("handles"); } -Variant CollisionShapeSpatialGizmo::get_handle_value(int p_idx) const { - - Ref<Shape> s = cs->get_shape(); - if (s.is_null()) - return Variant(); - - if (Object::cast_to<SphereShape>(*s)) { - - Ref<SphereShape> ss = s; - return ss->get_radius(); - } - - if (Object::cast_to<BoxShape>(*s)) { - - Ref<BoxShape> bs = s; - return bs->get_extents(); - } - - if (Object::cast_to<CapsuleShape>(*s)) { - - Ref<CapsuleShape> cs = s; - return p_idx == 0 ? cs->get_radius() : cs->get_height(); - } - - if (Object::cast_to<CylinderShape>(*s)) { - Ref<CylinderShape> cs = s; - return p_idx == 0 ? cs->get_radius() : cs->get_height(); - } - - if (Object::cast_to<RayShape>(*s)) { - - Ref<RayShape> cs = s; - return cs->get_length(); - } - - return Variant(); +bool SoftBodySpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<SoftBody>(p_spatial) != NULL; } -void CollisionShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { - Ref<Shape> s = cs->get_shape(); - if (s.is_null()) - return; - - Transform gt = cs->get_global_transform(); - gt.orthonormalize(); - Transform gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - if (Object::cast_to<SphereShape>(*s)) { - - Ref<SphereShape> ss = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); - float d = ra.x; - if (d < 0.001) - d = 0.001; - - ss->set_radius(d); - } - if (Object::cast_to<RayShape>(*s)) { - - Ref<RayShape> rs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); - float d = ra.z; - if (d < 0.001) - d = 0.001; - - rs->set_length(d); - } - - if (Object::cast_to<BoxShape>(*s)) { - - Vector3 axis; - axis[p_idx] = 1.0; - Ref<BoxShape> bs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (d < 0.001) - d = 0.001; - - Vector3 he = bs->get_extents(); - he[p_idx] = d; - bs->set_extents(he); - } - - if (Object::cast_to<CapsuleShape>(*s)) { - - Vector3 axis; - axis[p_idx == 0 ? 0 : 2] = 1.0; - Ref<CapsuleShape> cs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (p_idx == 1) - d -= cs->get_radius(); - if (d < 0.001) - d = 0.001; - - if (p_idx == 0) - cs->set_radius(d); - else if (p_idx == 1) - cs->set_height(d * 2.0); - } - - if (Object::cast_to<CylinderShape>(*s)) { - - Vector3 axis; - axis[p_idx == 0 ? 0 : 1] = 1.0; - Ref<CylinderShape> cs = s; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - - if (d < 0.001) - d = 0.001; - - if (p_idx == 0) - cs->set_radius(d); - else if (p_idx == 1) - cs->set_height(d * 2.0); - } +String SoftBodySpatialGizmoPlugin::get_name() const { + return "SoftBody"; } -void CollisionShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { - Ref<Shape> s = cs->get_shape(); - if (s.is_null()) - return; - - if (Object::cast_to<SphereShape>(*s)) { - - Ref<SphereShape> ss = s; - if (p_cancel) { - ss->set_radius(p_restore); - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Sphere Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - ur->commit_action(); - } - - if (Object::cast_to<BoxShape>(*s)) { - - Ref<BoxShape> ss = s; - if (p_cancel) { - ss->set_extents(p_restore); - return; - } - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Extents")); - ur->add_do_method(ss.ptr(), "set_extents", ss->get_extents()); - ur->add_undo_method(ss.ptr(), "set_extents", p_restore); - ur->commit_action(); - } - - if (Object::cast_to<CapsuleShape>(*s)) { - - Ref<CapsuleShape> ss = s; - if (p_cancel) { - if (p_idx == 0) - ss->set_radius(p_restore); - else - ss->set_height(p_restore); - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Capsule Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action(TTR("Change Capsule Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to<CylinderShape>(*s)) { - - Ref<CylinderShape> ss = s; - if (p_cancel) { - if (p_idx == 0) - ss->set_radius(p_restore); - else - ss->set_height(p_restore); - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - if (p_idx == 0) { - ur->create_action(TTR("Change Cylinder Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action(TTR("Change Cylinder Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to<RayShape>(*s)) { - - Ref<RayShape> ss = s; - if (p_cancel) { - ss->set_length(p_restore); - return; - } - - UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Ray Shape Length")); - ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); - ur->add_undo_method(ss.ptr(), "set_length", p_restore); - ur->commit_action(); - } +bool SoftBodySpatialGizmoPlugin::is_selectable_when_hidden() const { + return true; } -void CollisionShapeSpatialGizmo::redraw() { - - clear(); - - Ref<Shape> s = cs->get_shape(); - if (s.is_null()) - return; - - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape"); - Ref<Material> material = create_material("shape_material", gizmo_color); - - if (Object::cast_to<SphereShape>(*s)) { - - Ref<SphereShape> sp = s; - float r = sp->get_radius(); - - Vector<Vector3> points; - - for (int i = 0; i <= 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - } - - Vector<Vector3> collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - collision_segments.push_back(Vector3(a.x, 0, a.y)); - collision_segments.push_back(Vector3(b.x, 0, b.y)); - collision_segments.push_back(Vector3(0, a.x, a.y)); - collision_segments.push_back(Vector3(0, b.x, b.y)); - collision_segments.push_back(Vector3(a.x, a.y, 0)); - collision_segments.push_back(Vector3(b.x, b.y, 0)); - } - - add_lines(points, material); - add_collision_segments(collision_segments); - Vector<Vector3> handles; - handles.push_back(Vector3(r, 0, 0)); - add_handles(handles); - } - - if (Object::cast_to<BoxShape>(*s)) { - - Ref<BoxShape> bs = s; - Vector<Vector3> lines; - AABB aabb; - aabb.position = -bs->get_extents(); - aabb.size = aabb.position * -2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector<Vector3> handles; - - for (int i = 0; i < 3; i++) { - - Vector3 ax; - ax[i] = bs->get_extents()[i]; - handles.push_back(ax); - } - - add_lines(lines, material); - add_collision_segments(lines); - add_handles(handles); - } - - if (Object::cast_to<CapsuleShape>(*s)) { - - Ref<CapsuleShape> cs = s; - float radius = cs->get_radius(); - float height = cs->get_height(); - - Vector<Vector3> points; - - Vector3 d(0, 0, height * 0.5); - for (int i = 0; i < 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, a.y, 0) + d); - points.push_back(Vector3(b.x, b.y, 0) + d); - - points.push_back(Vector3(a.x, a.y, 0) - d); - points.push_back(Vector3(b.x, b.y, 0) - d); - - if (i % 90 == 0) { - - points.push_back(Vector3(a.x, a.y, 0) + d); - points.push_back(Vector3(a.x, a.y, 0) - d); - } - - Vector3 dud = i < 180 ? d : -d; - - points.push_back(Vector3(0, a.y, a.x) + dud); - points.push_back(Vector3(0, b.y, b.x) + dud); - points.push_back(Vector3(a.y, 0, a.x) + dud); - points.push_back(Vector3(b.y, 0, b.x) + dud); - } - - add_lines(points, material); - - Vector<Vector3> collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - collision_segments.push_back(Vector3(a.x, a.y, 0) + d); - collision_segments.push_back(Vector3(b.x, b.y, 0) + d); - - collision_segments.push_back(Vector3(a.x, a.y, 0) - d); - collision_segments.push_back(Vector3(b.x, b.y, 0) - d); - - if (i % 16 == 0) { - - collision_segments.push_back(Vector3(a.x, a.y, 0) + d); - collision_segments.push_back(Vector3(a.x, a.y, 0) - d); - } - - Vector3 dud = i < 32 ? d : -d; - - collision_segments.push_back(Vector3(0, a.y, a.x) + dud); - collision_segments.push_back(Vector3(0, b.y, b.x) + dud); - collision_segments.push_back(Vector3(a.y, 0, a.x) + dud); - collision_segments.push_back(Vector3(b.y, 0, b.x) + dud); - } - - add_collision_segments(collision_segments); - - Vector<Vector3> handles; - handles.push_back(Vector3(cs->get_radius(), 0, 0)); - handles.push_back(Vector3(0, 0, cs->get_height() * 0.5 + cs->get_radius())); - add_handles(handles); - } - - if (Object::cast_to<CylinderShape>(*s)) { - - Ref<CylinderShape> cs = s; - float radius = cs->get_radius(); - float height = cs->get_height(); - - Vector<Vector3> points; - - Vector3 d(0, height * 0.5, 0); - for (int i = 0; i < 360; i++) { - - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(b.x, 0, b.y) + d); - - points.push_back(Vector3(a.x, 0, a.y) - d); - points.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 90 == 0) { - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(a.x, 0, a.y) - d); - } - } - - add_lines(points, material); - - Vector<Vector3> collision_segments; - - for (int i = 0; i < 64; i++) { - - float ra = i * Math_PI * 2.0 / 64.0; - float rb = (i + 1) * Math_PI * 2.0 / 64.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(b.x, 0, b.y) + d); - - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - collision_segments.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 16 == 0) { - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - } - } +void SoftBodySpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + SoftBody *soft_body = Object::cast_to<SoftBody>(p_gizmo->get_spatial_node()); - add_collision_segments(collision_segments); + p_gizmo->clear(); - Vector<Vector3> handles; - handles.push_back(Vector3(cs->get_radius(), 0, 0)); - handles.push_back(Vector3(0, cs->get_height() * 0.5, 0)); - add_handles(handles); + if (!soft_body || soft_body->get_mesh().is_null()) { + return; } - if (Object::cast_to<PlaneShape>(*s)) { + // find mesh - Ref<PlaneShape> ps = s; - Plane p = ps->get_plane(); - Vector<Vector3> points; - - Vector3 n1 = p.get_any_perpendicular_normal(); - Vector3 n2 = p.normal.cross(n1).normalized(); + Vector<Vector3> lines; - Vector3 pface[4] = { - p.normal * p.d + n1 * 10.0 + n2 * 10.0, - p.normal * p.d + n1 * 10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * 10.0, - }; + soft_body->get_mesh()->generate_debug_mesh_lines(lines); - points.push_back(pface[0]); - points.push_back(pface[1]); - points.push_back(pface[1]); - points.push_back(pface[2]); - points.push_back(pface[2]); - points.push_back(pface[3]); - points.push_back(pface[3]); - points.push_back(pface[0]); - points.push_back(p.normal * p.d); - points.push_back(p.normal * p.d + p.normal * 3); - - add_lines(points, material); - add_collision_segments(points); + if (!lines.size()) { + return; } - if (Object::cast_to<ConvexPolygonShape>(*s)) { - - PoolVector<Vector3> points = Object::cast_to<ConvexPolygonShape>(*s)->get_points(); - - if (points.size() > 3) { - - QuickHull qh; - Vector<Vector3> varr = Variant(points); - Geometry::MeshData md; - Error err = qh.build(varr, md); - if (err == OK) { - Vector<Vector3> points; - points.resize(md.edges.size() * 2); - for (int i = 0; i < md.edges.size(); i++) { - points[i * 2 + 0] = md.vertices[md.edges[i].a]; - points[i * 2 + 1] = md.vertices[md.edges[i].b]; - } - - add_lines(points, material); - add_collision_segments(points); - } - } - } + Ref<TriangleMesh> tm = soft_body->get_mesh()->generate_triangle_mesh(); - if (Object::cast_to<RayShape>(*s)) { + Vector<Vector3> points; + soft_body->get_mesh()->generate_debug_mesh_indices(points); + soft_body->get_mesh()->clear_cache(); - Ref<RayShape> rs = s; + Ref<Material> material = get_material("shape_material", p_gizmo); - Vector<Vector3> points; - points.push_back(Vector3()); - points.push_back(Vector3(0, 0, rs->get_length())); - add_lines(points, material); - add_collision_segments(points); - Vector<Vector3> handles; - handles.push_back(Vector3(0, 0, rs->get_length())); - add_handles(handles); - } + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(points, get_material("handles")); + p_gizmo->add_collision_triangles(tm); } -CollisionShapeSpatialGizmo::CollisionShapeSpatialGizmo(CollisionShape *p_cs) { - cs = p_cs; - set_spatial_node(p_cs); +String SoftBodySpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + return "SoftBody pin point"; } -///// - -void CollisionPolygonSpatialGizmo::redraw() { - - clear(); - - Vector<Vector2> points = polygon->get_polygon(); - float depth = polygon->get_depth() * 0.5; +Variant SoftBodySpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + SoftBody *soft_body = Object::cast_to<SoftBody>(p_gizmo->get_spatial_node()); + return Variant(soft_body->is_point_pinned(p_idx)); +} - Vector<Vector3> lines; - for (int i = 0; i < points.size(); i++) { +void SoftBodySpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + SoftBody *soft_body = Object::cast_to<SoftBody>(p_gizmo->get_spatial_node()); + soft_body->pin_point_toggle(p_idx); +} - int n = (i + 1) % points.size(); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[n].x, points[n].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - lines.push_back(Vector3(points[n].x, points[n].y, -depth)); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - } +bool SoftBodySpatialGizmoPlugin::is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int idx) const { + SoftBody *soft_body = Object::cast_to<SoftBody>(p_gizmo->get_spatial_node()); + return soft_body->is_point_pinned(idx); +} - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape"); - Ref<Material> material = create_material("shape_material", gizmo_color); +/////////// - add_lines(lines, material); - add_collision_segments(lines); +VisibilityNotifierGizmoPlugin::VisibilityNotifierGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); + create_material("visibility_notifier_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("visibility_notifier_solid_material", gizmo_color); + create_handle_material("handles"); } -CollisionPolygonSpatialGizmo::CollisionPolygonSpatialGizmo(CollisionPolygon *p_polygon) { +bool VisibilityNotifierGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<VisibilityNotifier>(p_spatial) != NULL; +} - set_spatial_node(p_polygon); - polygon = p_polygon; +String VisibilityNotifierGizmoPlugin::get_name() const { + return "VisibilityNotifier"; } -/// -String VisibilityNotifierGizmo::get_handle_name(int p_idx) const { +String VisibilityNotifierGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { switch (p_idx) { - case 0: return "X"; - case 1: return "Y"; - case 2: return "Z"; + case 0: return "Size X"; + case 1: return "Size Y"; + case 2: return "Size Z"; + case 3: return "Pos X"; + case 4: return "Pos Y"; + case 5: return "Pos Z"; } return ""; } -Variant VisibilityNotifierGizmo::get_handle_value(int p_idx) const { +Variant VisibilityNotifierGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + + VisibilityNotifier *notifier = Object::cast_to<VisibilityNotifier>(p_gizmo->get_spatial_node()); return notifier->get_aabb(); } -void VisibilityNotifierGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void VisibilityNotifierGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + VisibilityNotifier *notifier = Object::cast_to<VisibilityNotifier>(p_gizmo->get_spatial_node()); Transform gt = notifier->get_global_transform(); - //gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + bool move = p_idx >= 3; + p_idx = p_idx % 3; + AABB aabb = notifier->get_aabb(); Vector3 ray_from = p_camera->project_ray_origin(p_point); Vector3 ray_dir = p_camera->project_ray_normal(p_point); Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + Vector3 ofs = aabb.position + aabb.size * 0.5; Vector3 axis; axis[p_idx] = 1.0; - Vector3 ra, rb; - Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - if (d < 0.001) - d = 0.001; + if (move) { - aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; - aabb.size[p_idx] = d * 2; - notifier->set_aabb(aabb); + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx]; + + aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; + notifier->set_aabb(aabb); + + } else { + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); + + float d = ra[p_idx] - ofs[p_idx]; + if (d < 0.001) + d = 0.001; + //resize + aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; + aabb.size[p_idx] = d * 2; + notifier->set_aabb(aabb); + } } -void VisibilityNotifierGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void VisibilityNotifierGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + VisibilityNotifier *notifier = Object::cast_to<VisibilityNotifier>(p_gizmo->get_spatial_node()); if (p_cancel) { notifier->set_aabb(p_restore); @@ -2557,18 +2153,17 @@ void VisibilityNotifierGizmo::commit_handle(int p_idx, const Variant &p_restore, } UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Notifier Extents")); + ur->create_action(TTR("Change Notifier AABB")); ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); ur->add_undo_method(notifier, "set_aabb", p_restore); ur->commit_action(); } -void VisibilityNotifierGizmo::redraw() { +void VisibilityNotifierGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/visibility_notifier"); - Ref<Material> material = create_material("visibility_notifier_material", gizmo_color); + VisibilityNotifier *notifier = Object::cast_to<VisibilityNotifier>(p_gizmo->get_spatial_node()); - clear(); + p_gizmo->clear(); Vector<Vector3> lines; AABB aabb = notifier->get_aabb(); @@ -2586,25 +2181,58 @@ void VisibilityNotifierGizmo::redraw() { Vector3 ax; ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; handles.push_back(ax); } - add_lines(lines, material); - //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05); - add_collision_segments(lines); - add_handles(handles); + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref<Material> material = get_material("visibility_notifier_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + + if (p_gizmo->is_selected()) { + Ref<Material> solid_material = get_material("visibility_notifier_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// + +ParticlesGizmoPlugin::ParticlesGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + create_material("particles_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("particles_solid_material", gizmo_color); + create_icon_material("particles_icon", SpatialEditor::get_singleton()->get_icon("GizmoParticles", "EditorIcons")); + create_handle_material("handles"); } -VisibilityNotifierGizmo::VisibilityNotifierGizmo(VisibilityNotifier *p_notifier) { - notifier = p_notifier; - set_spatial_node(p_notifier); +bool ParticlesGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Particles>(p_spatial) != NULL; } -//////// +String ParticlesGizmoPlugin::get_name() const { + return "Particles"; +} -/// +bool ParticlesGizmoPlugin::is_selectable_when_hidden() const { + return true; +} -String ParticlesGizmo::get_handle_name(int p_idx) const { +String ParticlesGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { switch (p_idx) { case 0: return "Size X"; @@ -2617,11 +2245,14 @@ String ParticlesGizmo::get_handle_name(int p_idx) const { return ""; } -Variant ParticlesGizmo::get_handle_value(int p_idx) const { +Variant ParticlesGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + Particles *particles = Object::cast_to<Particles>(p_gizmo->get_spatial_node()); return particles->get_visibility_aabb(); } -void ParticlesGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void ParticlesGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + Particles *particles = Object::cast_to<Particles>(p_gizmo->get_spatial_node()); Transform gt = particles->get_global_transform(); //gt.orthonormalize(); @@ -2665,7 +2296,9 @@ void ParticlesGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_poi } } -void ParticlesGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void ParticlesGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + Particles *particles = Object::cast_to<Particles>(p_gizmo->get_spatial_node()); if (p_cancel) { particles->set_visibility_aabb(p_restore); @@ -2679,9 +2312,11 @@ void ParticlesGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_c ur->commit_action(); } -void ParticlesGizmo::redraw() { +void ParticlesGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + Particles *particles = Object::cast_to<Particles>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Vector<Vector3> lines; AABB aabb = particles->get_visibility_aabb(); @@ -2714,36 +2349,46 @@ void ParticlesGizmo::redraw() { lines.push_back(center + ax); } - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/particles"); - Ref<Material> material = create_material("particles_material", gizmo_color); - Ref<Material> icon = create_icon_material("particles_icon", SpatialEditor::get_singleton()->get_icon("GizmoParticles", "EditorIcons")); - - add_lines(lines, material); - add_collision_segments(lines); + Ref<Material> material = get_material("particles_material", p_gizmo); + Ref<Material> icon = get_material("particles_icon", p_gizmo); - if (is_selected()) { + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); - gizmo_color.a = 0.1; - Ref<Material> solid_material = create_material("particles_solid_material", gizmo_color); - add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + if (p_gizmo->is_selected()) { + Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); } - //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05); - - add_handles(handles); - add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05); } -ParticlesGizmo::ParticlesGizmo(Particles *p_particles) { +//// + +ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); + + create_material("reflection_probe_material", gizmo_color); - particles = p_particles; - set_spatial_node(p_particles); + gizmo_color.a = 0.5; + create_material("reflection_internal_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("reflection_probe_solid_material", gizmo_color); + + create_icon_material("reflection_probe_icon", SpatialEditor::get_singleton()->get_icon("GizmoReflectionProbe", "EditorIcons")); + create_handle_material("handles"); } -//////// +bool ReflectionProbeGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<ReflectionProbe>(p_spatial) != NULL; +} -/// +String ReflectionProbeGizmoPlugin::get_name() const { + return "ReflectionProbe"; +} -String ReflectionProbeGizmo::get_handle_name(int p_idx) const { +String ReflectionProbeGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { switch (p_idx) { case 0: return "Extents X"; @@ -2756,14 +2401,16 @@ String ReflectionProbeGizmo::get_handle_name(int p_idx) const { return ""; } -Variant ReflectionProbeGizmo::get_handle_value(int p_idx) const { +Variant ReflectionProbeGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); return AABB(probe->get_extents(), probe->get_origin_offset()); } -void ReflectionProbeGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void ReflectionProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); Transform gt = probe->get_global_transform(); - //gt.orthonormalize(); + Transform gi = gt.affine_inverse(); if (p_idx < 3) { @@ -2810,7 +2457,9 @@ void ReflectionProbeGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 } } -void ReflectionProbeGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void ReflectionProbeGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); AABB restore = p_restore; @@ -2829,9 +2478,11 @@ void ReflectionProbeGizmo::commit_handle(int p_idx, const Variant &p_restore, bo ur->commit_action(); } -void ReflectionProbeGizmo::redraw() { +void ReflectionProbeGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); Vector<Vector3> lines; Vector<Vector3> internal_lines; @@ -2874,40 +2525,47 @@ void ReflectionProbeGizmo::redraw() { lines.push_back(orig_handle); } - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/reflection_probe"); - Ref<Material> material = create_material("reflection_probe_material", gizmo_color); - Ref<Material> icon = create_icon_material("reflection_probe_icon", SpatialEditor::get_singleton()->get_icon("GizmoReflectionProbe", "EditorIcons")); - - Color gizmo_color_internal = gizmo_color; - gizmo_color_internal.a = 0.5; - Ref<Material> material_internal = create_material("reflection_internal_material", gizmo_color_internal); - - add_lines(lines, material); - add_lines(internal_lines, material_internal); + Ref<Material> material = get_material("reflection_probe_material", p_gizmo); + Ref<Material> material_internal = get_material("reflection_internal_material", p_gizmo); + Ref<Material> icon = get_material("reflection_probe_icon", p_gizmo); - if (is_selected()) { + p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(internal_lines, material_internal); - gizmo_color.a = 0.1; - Ref<Material> solid_material = create_material("reflection_probe_solid_material", gizmo_color); - add_solid_box(solid_material, probe->get_extents() * 2.0); + if (p_gizmo->is_selected()) { + Ref<Material> solid_material = get_material("reflection_probe_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, probe->get_extents() * 2.0); } - //add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05); - add_unscaled_billboard(icon, 0.05); - add_collision_segments(lines); - add_handles(handles); + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(handles, get_material("handles")); } -ReflectionProbeGizmo::ReflectionProbeGizmo(ReflectionProbe *p_probe) { - probe = p_probe; - set_spatial_node(p_probe); +GIProbeGizmoPlugin::GIProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6)); + + create_material("gi_probe_material", gizmo_color); + + gizmo_color.a = 0.5; + create_material("gi_probe_internal_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("gi_probe_solid_material", gizmo_color); + + create_icon_material("gi_probe_icon", SpatialEditor::get_singleton()->get_icon("GizmoGIProbe", "EditorIcons")); + create_handle_material("handles"); } -//////// +bool GIProbeGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<GIProbe>(p_spatial) != NULL; +} -/// +String GIProbeGizmoPlugin::get_name() const { + return "GIProbe"; +} -String GIProbeGizmo::get_handle_name(int p_idx) const { +String GIProbeGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { switch (p_idx) { case 0: return "Extents X"; @@ -2917,11 +2575,14 @@ String GIProbeGizmo::get_handle_name(int p_idx) const { return ""; } -Variant GIProbeGizmo::get_handle_value(int p_idx) const { +Variant GIProbeGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + GIProbe *probe = Object::cast_to<GIProbe>(p_gizmo->get_spatial_node()); return probe->get_extents(); } -void GIProbeGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void GIProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + GIProbe *probe = Object::cast_to<GIProbe>(p_gizmo->get_spatial_node()); Transform gt = probe->get_global_transform(); //gt.orthonormalize(); @@ -2947,7 +2608,9 @@ void GIProbeGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point probe->set_extents(extents); } -void GIProbeGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void GIProbeGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + GIProbe *probe = Object::cast_to<GIProbe>(p_gizmo->get_spatial_node()); Vector3 restore = p_restore; @@ -2963,16 +2626,15 @@ void GIProbeGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_can ur->commit_action(); } -void GIProbeGizmo::redraw() { +void GIProbeGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/gi_probe"); - Ref<Material> material = create_material("gi_probe_material", gizmo_color); - Ref<Material> icon = create_icon_material("gi_probe_icon", SpatialEditor::get_singleton()->get_icon("GizmoGIProbe", "EditorIcons")); - Color gizmo_color_internal = gizmo_color; - gizmo_color_internal.a = 0.1; - Ref<Material> material_internal = create_material("gi_probe_internal_material", gizmo_color_internal); + GIProbe *probe = Object::cast_to<GIProbe>(p_gizmo->get_spatial_node()); - clear(); + Ref<Material> material = get_material("gi_probe_material", p_gizmo); + Ref<Material> icon = get_material("gi_probe_icon", p_gizmo); + Ref<Material> material_internal = get_material("gi_probe_internal_material", p_gizmo); + + p_gizmo->clear(); Vector<Vector3> lines; Vector3 extents = probe->get_extents(); @@ -2990,8 +2652,8 @@ void GIProbeGizmo::redraw() { lines.push_back(b); } - add_lines(lines, material); - add_collision_segments(lines); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); lines.clear(); @@ -3035,7 +2697,7 @@ void GIProbeGizmo::redraw() { } } - add_lines(lines, material_internal); + p_gizmo->add_lines(lines, material_internal); Vector<Vector3> handles; @@ -3046,28 +2708,30 @@ void GIProbeGizmo::redraw() { handles.push_back(ax); } - if (is_selected()) { - - gizmo_color.a = 0.1; - Ref<Material> solid_material = create_material("gi_probe_solid_material", gizmo_color); - add_solid_box(solid_material, aabb.get_size()); + if (p_gizmo->is_selected()) { + Ref<Material> solid_material = get_material("gi_probe_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size()); } - add_unscaled_billboard(icon, 0.05); - add_handles(handles); + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); } -GIProbeGizmo::GIProbeGizmo(GIProbe *p_probe) { - probe = p_probe; - set_spatial_node(p_probe); -} +//// -//////// -//////// +BakedIndirectLightGizmoPlugin::BakedIndirectLightGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1)); -/// + create_material("baked_indirect_light_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("baked_indirect_light_internal_material", gizmo_color); + + create_icon_material("baked_indirect_light_icon", SpatialEditor::get_singleton()->get_icon("GizmoBakedLightmap", "EditorIcons")); + create_handle_material("handles"); +} -String BakedIndirectLightGizmo::get_handle_name(int p_idx) const { +String BakedIndirectLightGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { switch (p_idx) { case 0: return "Extents X"; @@ -3077,11 +2741,14 @@ String BakedIndirectLightGizmo::get_handle_name(int p_idx) const { return ""; } -Variant BakedIndirectLightGizmo::get_handle_value(int p_idx) const { +Variant BakedIndirectLightGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node()); return baker->get_extents(); } -void BakedIndirectLightGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void BakedIndirectLightGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node()); Transform gt = baker->get_global_transform(); //gt.orthonormalize(); @@ -3107,7 +2774,9 @@ void BakedIndirectLightGizmo::set_handle(int p_idx, Camera *p_camera, const Poin baker->set_extents(extents); } -void BakedIndirectLightGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void BakedIndirectLightGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node()); Vector3 restore = p_restore; @@ -3123,16 +2792,23 @@ void BakedIndirectLightGizmo::commit_handle(int p_idx, const Variant &p_restore, ur->commit_action(); } -void BakedIndirectLightGizmo::redraw() { +bool BakedIndirectLightGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<BakedLightmap>(p_spatial) != NULL; +} + +String BakedIndirectLightGizmoPlugin::get_name() const { + return "BakedLightmap"; +} - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/baked_indirect_light"); - Ref<Material> material = create_material("baked_indirect_light_material", gizmo_color); - Ref<Material> icon = create_icon_material("baked_indirect_light_icon", SpatialEditor::get_singleton()->get_icon("GizmoBakedLightmap", "EditorIcons")); - Color gizmo_color_internal = gizmo_color; - gizmo_color_internal.a = 0.1; - Ref<Material> material_internal = create_material("baked_indirect_light_internal_material", gizmo_color_internal); +void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { - clear(); + BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node()); + + Ref<Material> material = get_material("baked_indirect_light_material", p_gizmo); + Ref<Material> icon = get_material("baked_indirect_light_icon", p_gizmo); + Ref<Material> material_internal = get_material("baked_indirect_light_internal_material", p_gizmo); + + p_gizmo->clear(); Vector<Vector3> lines; Vector3 extents = baker->get_extents(); @@ -3146,8 +2822,8 @@ void BakedIndirectLightGizmo::redraw() { lines.push_back(b); } - add_lines(lines, material); - add_collision_segments(lines); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); Vector<Vector3> handles; @@ -3158,31 +2834,670 @@ void BakedIndirectLightGizmo::redraw() { handles.push_back(ax); } - if (is_selected()) { + if (p_gizmo->is_selected()) { + p_gizmo->add_solid_box(material_internal, aabb.get_size()); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// + +CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + create_handle_material("handles"); +} + +bool CollisionShapeSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<CollisionShape>(p_spatial) != NULL; +} + +String CollisionShapeSpatialGizmoPlugin::get_name() const { + return "CollisionShape"; +} + +String CollisionShapeSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + + const CollisionShape *cs = Object::cast_to<CollisionShape>(p_gizmo->get_spatial_node()); + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return ""; + + if (Object::cast_to<SphereShape>(*s)) { + + return "Radius"; + } + + if (Object::cast_to<BoxShape>(*s)) { - gizmo_color.a = 0.1; - Ref<Material> solid_material = create_material("baked_indirect_light_solid_material", gizmo_color); - add_solid_box(solid_material, aabb.get_size()); + return "Extents"; } - add_unscaled_billboard(icon, 0.05); - add_handles(handles); + if (Object::cast_to<CapsuleShape>(*s)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to<CylinderShape>(*s)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to<RayShape>(*s)) { + + return "Length"; + } + + return ""; } -BakedIndirectLightGizmo::BakedIndirectLightGizmo(BakedLightmap *p_baker) { - baker = p_baker; - set_spatial_node(p_baker); +Variant CollisionShapeSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + + CollisionShape *cs = Object::cast_to<CollisionShape>(p_gizmo->get_spatial_node()); + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return Variant(); + + if (Object::cast_to<SphereShape>(*s)) { + + Ref<SphereShape> ss = s; + return ss->get_radius(); + } + + if (Object::cast_to<BoxShape>(*s)) { + + Ref<BoxShape> bs = s; + return bs->get_extents(); + } + + if (Object::cast_to<CapsuleShape>(*s)) { + + Ref<CapsuleShape> cs = s; + return p_idx == 0 ? cs->get_radius() : cs->get_height(); + } + + if (Object::cast_to<CylinderShape>(*s)) { + + Ref<CylinderShape> cs = s; + return p_idx == 0 ? cs->get_radius() : cs->get_height(); + } + + if (Object::cast_to<RayShape>(*s)) { + + Ref<RayShape> cs = s; + return cs->get_length(); + } + + return Variant(); } +void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { -//////// -void NavigationMeshSpatialGizmo::redraw() { + CollisionShape *cs = Object::cast_to<CollisionShape>(p_gizmo->get_spatial_node()); - Ref<Material> edge_material = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_edge")); - Ref<Material> edge_material_disabled = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled")); - Ref<Material> solid_material = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_solid")); - Ref<Material> solid_material_disabled = create_material("navigation_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled")); + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; - clear(); + Transform gt = cs->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + if (Object::cast_to<SphereShape>(*s)) { + + Ref<SphereShape> ss = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (d < 0.001) + d = 0.001; + + ss->set_radius(d); + } + + if (Object::cast_to<RayShape>(*s)) { + + Ref<RayShape> rs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); + float d = ra.z; + if (d < 0.001) + d = 0.001; + + rs->set_length(d); + } + + if (Object::cast_to<BoxShape>(*s)) { + + Vector3 axis; + axis[p_idx] = 1.0; + Ref<BoxShape> bs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (d < 0.001) + d = 0.001; + + Vector3 he = bs->get_extents(); + he[p_idx] = d; + bs->set_extents(he); + } + + if (Object::cast_to<CapsuleShape>(*s)) { + + Vector3 axis; + axis[p_idx == 0 ? 0 : 2] = 1.0; + Ref<CapsuleShape> cs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + if (p_idx == 1) + d -= cs->get_radius(); + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + cs->set_radius(d); + else if (p_idx == 1) + cs->set_height(d * 2.0); + } + + if (Object::cast_to<CylinderShape>(*s)) { + + Vector3 axis; + axis[p_idx == 0 ? 0 : 1] = 1.0; + Ref<CylinderShape> cs = s; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + cs->set_radius(d); + else if (p_idx == 1) + cs->set_height(d * 2.0); + } +} +void CollisionShapeSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + CollisionShape *cs = Object::cast_to<CollisionShape>(p_gizmo->get_spatial_node()); + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; + + if (Object::cast_to<SphereShape>(*s)) { + + Ref<SphereShape> ss = s; + if (p_cancel) { + ss->set_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Sphere Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to<BoxShape>(*s)) { + + Ref<BoxShape> ss = s; + if (p_cancel) { + ss->set_extents(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Extents")); + ur->add_do_method(ss.ptr(), "set_extents", ss->get_extents()); + ur->add_undo_method(ss.ptr(), "set_extents", p_restore); + ur->commit_action(); + } + + if (Object::cast_to<CapsuleShape>(*s)) { + + Ref<CapsuleShape> ss = s; + if (p_cancel) { + if (p_idx == 0) + ss->set_radius(p_restore); + else + ss->set_height(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Capsule Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + } else { + ur->create_action(TTR("Change Capsule Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to<CylinderShape>(*s)) { + + Ref<CylinderShape> ss = s; + if (p_cancel) { + if (p_idx == 0) + ss->set_radius(p_restore); + else + ss->set_height(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Cylinder Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + } else { + ur->create_action( + /// + + //////// + TTR("Change Cylinder Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to<RayShape>(*s)) { + + Ref<RayShape> ss = s; + if (p_cancel) { + ss->set_length(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Ray Shape Length")); + ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); + ur->add_undo_method(ss.ptr(), "set_length", p_restore); + ur->commit_action(); + } +} +void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + CollisionShape *cs = Object::cast_to<CollisionShape>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref<Shape> s = cs->get_shape(); + if (s.is_null()) + return; + + Ref<Material> material = get_material("shape_material", p_gizmo); + Ref<Material> handles_material = get_material("handles"); + + if (Object::cast_to<SphereShape>(*s)) { + + Ref<SphereShape> sp = s; + float r = sp->get_radius(); + + Vector<Vector3> points; + + for (int i = 0; i <= 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + } + + Vector<Vector3> collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + collision_segments.push_back(Vector3(a.x, 0, a.y)); + collision_segments.push_back(Vector3(b.x, 0, b.y)); + collision_segments.push_back(Vector3(0, a.x, a.y)); + collision_segments.push_back(Vector3(0, b.x, b.y)); + collision_segments.push_back(Vector3(a.x, a.y, 0)); + collision_segments.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(collision_segments); + Vector<Vector3> handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to<BoxShape>(*s)) { + + Ref<BoxShape> bs = s; + Vector<Vector3> lines; + AABB aabb; + aabb.position = -bs->get_extents(); + aabb.size = aabb.position * -2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector<Vector3> handles; + + for (int i = 0; i < 3; i++) { + + Vector3 ax; + ax[i] = bs->get_extents()[i]; + handles.push_back(ax); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to<CapsuleShape>(*s)) { + + Ref<CapsuleShape> cs = s; + float radius = cs->get_radius(); + float height = cs->get_height(); + + Vector<Vector3> points; + + Vector3 d(0, 0, height * 0.5); + for (int i = 0; i < 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, a.y, 0) + d); + points.push_back(Vector3(b.x, b.y, 0) + d); + + points.push_back(Vector3(a.x, a.y, 0) - d); + points.push_back(Vector3(b.x, b.y, 0) - d); + + if (i % 90 == 0) { + + points.push_back(Vector3(a.x, a.y, 0) + d); + points.push_back(Vector3(a.x, a.y, 0) - d); + } + + Vector3 dud = i < 180 ? d : -d; + + points.push_back(Vector3(0, a.y, a.x) + dud); + points.push_back(Vector3(0, b.y, b.x) + dud); + points.push_back(Vector3(a.y, 0, a.x) + dud); + points.push_back(Vector3(b.y, 0, b.x) + dud); + } + + p_gizmo->add_lines(points, material); + + Vector<Vector3> collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, a.y, 0) + d); + collision_segments.push_back(Vector3(b.x, b.y, 0) + d); + + collision_segments.push_back(Vector3(a.x, a.y, 0) - d); + collision_segments.push_back(Vector3(b.x, b.y, 0) - d); + + if (i % 16 == 0) { + + collision_segments.push_back(Vector3(a.x, a.y, 0) + d); + collision_segments.push_back(Vector3(a.x, a.y, 0) - d); + } + + Vector3 dud = i < 32 ? d : -d; + + collision_segments.push_back(Vector3(0, a.y, a.x) + dud); + collision_segments.push_back(Vector3(0, b.y, b.x) + dud); + collision_segments.push_back(Vector3(a.y, 0, a.x) + dud); + collision_segments.push_back(Vector3(b.y, 0, b.x) + dud); + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector<Vector3> handles; + handles.push_back(Vector3(cs->get_radius(), 0, 0)); + handles.push_back(Vector3(0, 0, cs->get_height() * 0.5 + cs->get_radius())); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to<CylinderShape>(*s)) { + + Ref<CylinderShape> cs = s; + float radius = cs->get_radius(); + float height = cs->get_height(); + + Vector<Vector3> points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_lines(points, material); + + Vector<Vector3> collision_segments; + + for (int i = 0; i < 64; i++) { + + float ra = i * Math_PI * 2.0 / 64.0; + float rb = (i + 1) * Math_PI * 2.0 / 64.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(b.x, 0, b.y) + d); + + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + collision_segments.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 16 == 0) { + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector<Vector3> handles; + handles.push_back(Vector3(cs->get_radius(), 0, 0)); + handles.push_back(Vector3(0, cs->get_height() * 0.5, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to<PlaneShape>(*s)) { + + Ref<PlaneShape> ps = s; + Plane p = ps->get_plane(); + Vector<Vector3> points; + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4] = { + p.normal * p.d + n1 * 10.0 + n2 * 10.0, + p.normal * p.d + n1 * 10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * 10.0, + }; + + points.push_back(pface[0]); + points.push_back(pface[1]); + points.push_back(pface[1]); + points.push_back(pface[2]); + points.push_back(pface[2]); + points.push_back(pface[3]); + points.push_back(pface[3]); + points.push_back(pface[0]); + points.push_back(p.normal * p.d); + points.push_back(p.normal * p.d + p.normal * 3); + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + } + + if (Object::cast_to<ConvexPolygonShape>(*s)) { + + PoolVector<Vector3> points = Object::cast_to<ConvexPolygonShape>(*s)->get_points(); + + if (points.size() > 3) { + + QuickHull qh; + Vector<Vector3> varr = Variant(points); + Geometry::MeshData md; + Error err = qh.build(varr, md); + if (err == OK) { + Vector<Vector3> points; + points.resize(md.edges.size() * 2); + for (int i = 0; i < md.edges.size(); i++) { + points.write[i * 2 + 0] = md.vertices[md.edges[i].a]; + points.write[i * 2 + 1] = md.vertices[md.edges[i].b]; + } + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + } + } + } + + if (Object::cast_to<RayShape>(*s)) { + + Ref<RayShape> rs = s; + + Vector<Vector3> points; + points.push_back(Vector3()); + points.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + Vector<Vector3> handles; + handles.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_handles(handles, handles_material); + } +} + +///// + +CollisionPolygonSpatialGizmoPlugin::CollisionPolygonSpatialGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool CollisionPolygonSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<CollisionPolygon>(p_spatial) != NULL; +} + +String CollisionPolygonSpatialGizmoPlugin::get_name() const { + return "CollisionPolygon"; +} + +void CollisionPolygonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + CollisionPolygon *polygon = Object::cast_to<CollisionPolygon>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector<Vector2> points = polygon->get_polygon(); + float depth = polygon->get_depth() * 0.5; + + Vector<Vector3> lines; + for (int i = 0; i < points.size(); i++) { + + int n = (i + 1) % points.size(); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[n].x, points[n].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + lines.push_back(Vector3(points[n].x, points[n].y, -depth)); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + } + + Ref<Material> material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +//// + +NavigationMeshSpatialGizmoPlugin::NavigationMeshSpatialGizmoPlugin() { + create_material("navigation_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1))); + create_material("navigation_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7))); + create_material("navigation_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4))); + create_material("navigation_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4))); +} + +bool NavigationMeshSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<NavigationMeshInstance>(p_spatial) != NULL; +} + +String NavigationMeshSpatialGizmoPlugin::get_name() const { + return "NavigationMeshInstance"; +} + +void NavigationMeshSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + NavigationMeshInstance *navmesh = Object::cast_to<NavigationMeshInstance>(p_gizmo->get_spatial_node()); + + Ref<Material> edge_material = get_material("navigation_material", p_gizmo); + Ref<Material> edge_material_disabled = get_material("navigation_material", p_gizmo); + Ref<Material> solid_material = get_material("navigation_material", p_gizmo); + Ref<Material> solid_material_disabled = get_material("navigation_material", p_gizmo); + + p_gizmo->clear(); Ref<NavigationMesh> navmeshie = navmesh->get_navigation_mesh(); if (navmeshie.is_null()) return; @@ -3254,28 +3569,19 @@ void NavigationMeshSpatialGizmo::redraw() { tmesh->create(tmeshfaces); if (lines.size()) - add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); - add_collision_triangles(tmesh); + p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); + p_gizmo->add_collision_triangles(tmesh); Ref<ArrayMesh> m = memnew(ArrayMesh); Array a; a.resize(Mesh::ARRAY_MAX); a[0] = tmeshfaces; m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); - add_mesh(m); - add_collision_segments(lines); -} - -NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh) { - - set_spatial_node(p_navmesh); - navmesh = p_navmesh; + p_gizmo->add_mesh(m); + p_gizmo->add_collision_segments(lines); } ////// -/// -/// -/// #define BODY_A_RADIUS 0.25 #define BODY_B_RADIUS 0.27 @@ -3526,38 +3832,169 @@ void JointGizmosDrawer::draw_cone(const Transform &p_offset, const Basis &p_base } } -void PinJointSpatialGizmo::CreateGizmo(const Transform &p_offset, Vector<Vector3> &r_cursor_points) { - float cs = 0.25; +//// - r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); +JointSpatialGizmoPlugin::JointSpatialGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); + create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); + create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); } -void PinJointSpatialGizmo::redraw() { +bool JointSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<Joint>(p_spatial) != NULL; +} - clear(); - Vector<Vector3> cursor_points; - CreateGizmo(Transform(), cursor_points); - add_collision_segments(cursor_points); +String JointSpatialGizmoPlugin::get_name() const { + return "Joints"; +} - Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); +void JointSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + Joint *joint = Object::cast_to<Joint>(p_gizmo->get_spatial_node()); - add_lines(cursor_points, material); -} + p_gizmo->clear(); + + const Spatial *node_body_a = Object::cast_to<Spatial>(joint->get_node(joint->get_node_a())); + const Spatial *node_body_b = Object::cast_to<Spatial>(joint->get_node(joint->get_node_b())); -PinJointSpatialGizmo::PinJointSpatialGizmo(PinJoint *p_p3d) { + Ref<Material> common_material = get_material("joint_material", p_gizmo); + Ref<Material> body_a_material = get_material("joint_body_a_material", p_gizmo); + Ref<Material> body_b_material = get_material("joint_body_b_material", p_gizmo); - p3d = p_p3d; - set_spatial_node(p3d); + Vector<Vector3> points; + Vector<Vector3> body_a_points; + Vector<Vector3> body_b_points; + + if (Object::cast_to<PinJoint>(joint)) { + CreatePinJointGizmo(Transform(), points); + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, common_material); + } + + HingeJoint *hinge = Object::cast_to<HingeJoint>(joint); + if (hinge) { + + CreateHingeJointGizmo( + Transform(), + hinge->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + hinge->get_param(HingeJoint::PARAM_LIMIT_LOWER), + hinge->get_param(HingeJoint::PARAM_LIMIT_UPPER), + hinge->get_flag(HingeJoint::FLAG_USE_LIMIT), + points, + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + SliderJoint *slider = Object::cast_to<SliderJoint>(joint); + if (slider) { + + CreateSliderJointGizmo( + Transform(), + slider->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + slider->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER), + slider->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER), + slider->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER), + slider->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER), + points, + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + ConeTwistJoint *cone = Object::cast_to<ConeTwistJoint>(joint); + if (cone) { + + CreateConeTwistJointGizmo( + Transform(), + cone->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + cone->get_param(ConeTwistJoint::PARAM_SWING_SPAN), + cone->get_param(ConeTwistJoint::PARAM_TWIST_SPAN), + node_body_a ? &body_a_points : NULL, + node_body_b ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + Generic6DOFJoint *gen = Object::cast_to<Generic6DOFJoint>(joint); + if (gen) { + + CreateGeneric6DOFJointGizmo( + Transform(), + gen->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform(), + node_body_b ? node_body_b->get_global_transform() : Transform(), + + gen->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), + + points, + node_body_a ? &body_a_points : NULL, + node_body_a ? &body_b_points : NULL); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } } -//// +void JointSpatialGizmoPlugin::CreatePinJointGizmo(const Transform &p_offset, Vector<Vector3> &r_cursor_points) { + float cs = 0.25; + + r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); +} -void HingeJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { +void JointSpatialGizmoPlugin::CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { r_common_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); r_common_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); @@ -3589,52 +4026,7 @@ void HingeJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Transf } } -void HingeJointSpatialGizmo::redraw() { - - const Spatial *node_body_a = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_a())); - const Spatial *node_body_b = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_b())); - - Vector<Vector3> points; - Vector<Vector3> body_a_points; - Vector<Vector3> body_b_points; - CreateGizmo( - Transform(), - p3d->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - p3d->get_param(HingeJoint::PARAM_LIMIT_LOWER), - p3d->get_param(HingeJoint::PARAM_LIMIT_UPPER), - p3d->get_flag(HingeJoint::FLAG_USE_LIMIT), - points, - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - clear(); - - Ref<Material> common_material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - Ref<Material> body_a_material = create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a")); - Ref<Material> body_b_material = create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b")); - - add_collision_segments(points); - add_collision_segments(body_a_points); - add_collision_segments(body_b_points); - - add_lines(points, common_material); - add_lines(body_a_points, body_a_material); - add_lines(body_b_points, body_b_material); -} - -HingeJointSpatialGizmo::HingeJointSpatialGizmo(HingeJoint *p_p3d) { - - p3d = p_p3d; - set_spatial_node(p3d); -} - -/////// -/// -//// - -void SliderJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { +void JointSpatialGizmoPlugin::CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { p_linear_limit_lower = -p_linear_limit_lower; p_linear_limit_upper = -p_linear_limit_upper; @@ -3694,53 +4086,7 @@ void SliderJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Trans true); } -void SliderJointSpatialGizmo::redraw() { - - const Spatial *node_body_a = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_a())); - const Spatial *node_body_b = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_b())); - - clear(); - Vector<Vector3> cursor_points; - Vector<Vector3> body_a_points; - Vector<Vector3> body_b_points; - - CreateGizmo( - Transform(), - p3d->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_LOWER), - p3d->get_param(SliderJoint::PARAM_ANGULAR_LIMIT_UPPER), - p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_LOWER), - p3d->get_param(SliderJoint::PARAM_LINEAR_LIMIT_UPPER), - cursor_points, - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - Ref<Material> body_a_material = create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a")); - Ref<Material> body_b_material = create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b")); - - add_collision_segments(cursor_points); - add_collision_segments(body_a_points); - add_collision_segments(body_b_points); - - add_lines(cursor_points, material); - add_lines(body_a_points, body_a_material); - add_lines(body_b_points, body_b_material); -} - -SliderJointSpatialGizmo::SliderJointSpatialGizmo(SliderJoint *p_p3d) { - - p3d = p_p3d; - set_spatial_node(p3d); -} - -/////// -/// -//// - -void ConeTwistJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { +void JointSpatialGizmoPlugin::CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points) { if (r_body_a_points) JointGizmosDrawer::draw_cone( @@ -3759,51 +4105,7 @@ void ConeTwistJointSpatialGizmo::CreateGizmo(const Transform &p_offset, const Tr *r_body_b_points); } -void ConeTwistJointSpatialGizmo::redraw() { - - const Spatial *node_body_a = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_a())); - const Spatial *node_body_b = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_b())); - - clear(); - Vector<Vector3> points; - Vector<Vector3> body_a_points; - Vector<Vector3> body_b_points; - - CreateGizmo( - Transform(), - p3d->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - p3d->get_param(ConeTwistJoint::PARAM_SWING_SPAN), - p3d->get_param(ConeTwistJoint::PARAM_TWIST_SPAN), - points, - node_body_a ? &body_a_points : NULL, - node_body_b ? &body_b_points : NULL); - - Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - Ref<Material> body_a_material = create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a")); - Ref<Material> body_b_material = create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b")); - - add_collision_segments(points); - add_collision_segments(body_a_points); - add_collision_segments(body_b_points); - - add_lines(points, material); - add_lines(body_a_points, body_a_material); - add_lines(body_b_points, body_b_material); -} - -ConeTwistJointSpatialGizmo::ConeTwistJointSpatialGizmo(ConeTwistJoint *p_p3d) { - - p3d = p_p3d; - set_spatial_node(p3d); -} - -/////// -/// -//// - -void Generic6DOFJointSpatialGizmo::CreateGizmo( +void JointSpatialGizmoPlugin::CreateGeneric6DOFJointGizmo( const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, @@ -3952,503 +4254,3 @@ void Generic6DOFJointSpatialGizmo::CreateGizmo( #undef ADD_VTX } - -void Generic6DOFJointSpatialGizmo::redraw() { - - const Spatial *node_body_a = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_a())); - const Spatial *node_body_b = Object::cast_to<Spatial>(p3d->get_node(p3d->get_node_b())); - - clear(); - Vector<Vector3> cursor_points; - Vector<Vector3> body_a_points; - Vector<Vector3> body_b_points; - - CreateGizmo( - Transform(), - p3d->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform(), - node_body_b ? node_body_b->get_global_transform() : Transform(), - - p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), - p3d->get_param_x(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), - p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), - p3d->get_param_x(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), - p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), - p3d->get_flag_x(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), - - p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), - p3d->get_param_y(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), - p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), - p3d->get_param_y(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), - p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), - p3d->get_flag_y(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), - - p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_LOWER_LIMIT), - p3d->get_param_z(Generic6DOFJoint::PARAM_ANGULAR_UPPER_LIMIT), - p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_LOWER_LIMIT), - p3d->get_param_z(Generic6DOFJoint::PARAM_LINEAR_UPPER_LIMIT), - p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_ANGULAR_LIMIT), - p3d->get_flag_z(Generic6DOFJoint::FLAG_ENABLE_LINEAR_LIMIT), - - cursor_points, - node_body_a ? &body_a_points : NULL, - node_body_a ? &body_b_points : NULL); - - Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint")); - Ref<Material> body_a_material = create_material("joint_body_a_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_a")); - Ref<Material> body_b_material = create_material("joint_body_b_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint_body_b")); - - add_collision_segments(cursor_points); - add_collision_segments(body_a_points); - add_collision_segments(body_b_points); - - add_lines(cursor_points, material); - add_lines(body_a_points, body_a_material); - add_lines(body_b_points, body_b_material); -} - -Generic6DOFJointSpatialGizmo::Generic6DOFJointSpatialGizmo(Generic6DOFJoint *p_p3d) { - - p3d = p_p3d; - set_spatial_node(p3d); -} - -/////// -/// -//// - -SpatialEditorGizmos *SpatialEditorGizmos::singleton = NULL; - -Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) { - - if (Object::cast_to<Light>(p_spatial)) { - - Ref<LightSpatialGizmo> lsg = memnew(LightSpatialGizmo(Object::cast_to<Light>(p_spatial))); - return lsg; - } - - if (Object::cast_to<Camera>(p_spatial)) { - - Ref<CameraSpatialGizmo> lsg = memnew(CameraSpatialGizmo(Object::cast_to<Camera>(p_spatial))); - return lsg; - } - - if (Object::cast_to<Skeleton>(p_spatial)) { - - Ref<SkeletonSpatialGizmo> lsg = memnew(SkeletonSpatialGizmo(Object::cast_to<Skeleton>(p_spatial))); - return lsg; - } - - if (Object::cast_to<PhysicalBone>(p_spatial)) { - - Ref<PhysicalBoneSpatialGizmo> pbsg = memnew(PhysicalBoneSpatialGizmo(Object::cast_to<PhysicalBone>(p_spatial))); - return pbsg; - } - - if (Object::cast_to<Position3D>(p_spatial)) { - - Ref<Position3DSpatialGizmo> lsg = memnew(Position3DSpatialGizmo(Object::cast_to<Position3D>(p_spatial))); - return lsg; - } - - if (Object::cast_to<MeshInstance>(p_spatial)) { - - Ref<MeshInstanceSpatialGizmo> misg = memnew(MeshInstanceSpatialGizmo(Object::cast_to<MeshInstance>(p_spatial))); - return misg; - } - - /*if (Object::cast_to<Room>(p_spatial)) { - - Ref<RoomSpatialGizmo> misg = memnew(RoomSpatialGizmo(Object::cast_to<Room>(p_spatial))); - return misg; - }*/ - - if (Object::cast_to<NavigationMeshInstance>(p_spatial)) { - - Ref<NavigationMeshSpatialGizmo> misg = memnew(NavigationMeshSpatialGizmo(Object::cast_to<NavigationMeshInstance>(p_spatial))); - return misg; - } - - if (Object::cast_to<RayCast>(p_spatial)) { - - Ref<RayCastSpatialGizmo> misg = memnew(RayCastSpatialGizmo(Object::cast_to<RayCast>(p_spatial))); - return misg; - } - /* - if (Object::cast_to<Portal>(p_spatial)) { - - Ref<PortalSpatialGizmo> misg = memnew(PortalSpatialGizmo(Object::cast_to<Portal>(p_spatial))); - return misg; - } -*/ - if (Object::cast_to<CollisionShape>(p_spatial)) { - - Ref<CollisionShapeSpatialGizmo> misg = memnew(CollisionShapeSpatialGizmo(Object::cast_to<CollisionShape>(p_spatial))); - return misg; - } - - if (Object::cast_to<VisibilityNotifier>(p_spatial)) { - - Ref<VisibilityNotifierGizmo> misg = memnew(VisibilityNotifierGizmo(Object::cast_to<VisibilityNotifier>(p_spatial))); - return misg; - } - - if (Object::cast_to<Particles>(p_spatial)) { - - Ref<ParticlesGizmo> misg = memnew(ParticlesGizmo(Object::cast_to<Particles>(p_spatial))); - return misg; - } - - if (Object::cast_to<ReflectionProbe>(p_spatial)) { - - Ref<ReflectionProbeGizmo> misg = memnew(ReflectionProbeGizmo(Object::cast_to<ReflectionProbe>(p_spatial))); - return misg; - } - if (Object::cast_to<GIProbe>(p_spatial)) { - - Ref<GIProbeGizmo> misg = memnew(GIProbeGizmo(Object::cast_to<GIProbe>(p_spatial))); - return misg; - } - if (Object::cast_to<BakedLightmap>(p_spatial)) { - - Ref<BakedIndirectLightGizmo> misg = memnew(BakedIndirectLightGizmo(Object::cast_to<BakedLightmap>(p_spatial))); - return misg; - } - - if (Object::cast_to<VehicleWheel>(p_spatial)) { - - Ref<VehicleWheelSpatialGizmo> misg = memnew(VehicleWheelSpatialGizmo(Object::cast_to<VehicleWheel>(p_spatial))); - return misg; - } - if (Object::cast_to<PinJoint>(p_spatial)) { - - Ref<PinJointSpatialGizmo> misg = memnew(PinJointSpatialGizmo(Object::cast_to<PinJoint>(p_spatial))); - return misg; - } - - if (Object::cast_to<HingeJoint>(p_spatial)) { - - Ref<HingeJointSpatialGizmo> misg = memnew(HingeJointSpatialGizmo(Object::cast_to<HingeJoint>(p_spatial))); - return misg; - } - - if (Object::cast_to<SliderJoint>(p_spatial)) { - - Ref<SliderJointSpatialGizmo> misg = memnew(SliderJointSpatialGizmo(Object::cast_to<SliderJoint>(p_spatial))); - return misg; - } - - if (Object::cast_to<ConeTwistJoint>(p_spatial)) { - - Ref<ConeTwistJointSpatialGizmo> misg = memnew(ConeTwistJointSpatialGizmo(Object::cast_to<ConeTwistJoint>(p_spatial))); - return misg; - } - - if (Object::cast_to<Generic6DOFJoint>(p_spatial)) { - - Ref<Generic6DOFJointSpatialGizmo> misg = memnew(Generic6DOFJointSpatialGizmo(Object::cast_to<Generic6DOFJoint>(p_spatial))); - return misg; - } - - if (Object::cast_to<CollisionPolygon>(p_spatial)) { - - Ref<CollisionPolygonSpatialGizmo> misg = memnew(CollisionPolygonSpatialGizmo(Object::cast_to<CollisionPolygon>(p_spatial))); - return misg; - } - - if (Object::cast_to<AudioStreamPlayer3D>(p_spatial)) { - - Ref<AudioStreamPlayer3DSpatialGizmo> misg = memnew(AudioStreamPlayer3DSpatialGizmo(Object::cast_to<AudioStreamPlayer3D>(p_spatial))); - return misg; - } - - return Ref<SpatialEditorGizmo>(); -} - -SpatialEditorGizmos::SpatialEditorGizmos() { - - singleton = this; - - handle_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - handle_material->set_on_top_of_alpha(); - handle_material->set_albedo(Color(0.8, 0.8, 0.8)); - handle_material_billboard = handle_material->duplicate(); - handle_material_billboard->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - - handle2_material = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - handle2_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - handle2_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true); - handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle", "EditorIcons"); - handle2_material->set_point_size(handle_t->get_width()); - handle2_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle_t); - handle2_material->set_albedo(Color(1, 1, 1)); - handle2_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - handle2_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - handle2_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - handle2_material->set_on_top_of_alpha(); - handle2_material_billboard = handle2_material->duplicate(); - handle2_material_billboard->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - handle2_material_billboard->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - handle2_material_billboard->set_on_top_of_alpha(); - - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/light", Color(1, 1, 0.2)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/gi_probe", Color(0.5, 1, 0.6)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/baked_indirect_light", Color(0.5, 0.6, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4)); - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.5)); - -#if 0 - light_material = create_line_material(Color(1, 1, 0.2)); - light_material_omni = create_line_material(Color(1, 1, 0.2)); - light_material_omni->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - - light_material_omni_icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - light_material_omni_icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - light_material_omni_icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - light_material_omni_icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - light_material_omni_icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - light_material_omni_icon->set_albedo(Color(1, 1, 1, 0.9)); - light_material_omni_icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, SpatialEditor::get_singleton()->get_icon("GizmoLight", "EditorIcons")); - light_material_omni_icon->set_flag(SpatialMaterial::FLAG_FIXED_SIZE, true); - light_material_omni_icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - - light_material_directional_icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - light_material_directional_icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - light_material_directional_icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - light_material_directional_icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - light_material_directional_icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - light_material_directional_icon->set_albedo(Color(1, 1, 1, 0.9)); - light_material_directional_icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, SpatialEditor::get_singleton()->get_icon("GizmoDirectionalLight", "EditorIcons")); - light_material_directional_icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); - light_material_directional_icon->set_depth_scale(1); - - camera_material = create_line_material(Color(1.0, 0.5, 1.0)); - - navmesh_edge_material = create_line_material(Color(0.1, 0.8, 1.0)); - navmesh_solid_material = create_solid_material(Color(0.1, 0.8, 1.0, 0.4)); - navmesh_edge_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, false); - navmesh_edge_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, false); - navmesh_solid_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); - - navmesh_edge_material_disabled = create_line_material(Color(1.0, 0.8, 0.1)); - navmesh_solid_material_disabled = create_solid_material(Color(1.0, 0.8, 0.1, 0.4)); - navmesh_edge_material_disabled->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, false); - navmesh_edge_material_disabled->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, false); - navmesh_solid_material_disabled->set_cull_mode(SpatialMaterial::CULL_DISABLED); - - skeleton_material = create_line_material(Color(0.6, 1.0, 0.3)); - skeleton_material->set_cull_mode(SpatialMaterial::CULL_DISABLED); - skeleton_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - skeleton_material->set_on_top_of_alpha(); - skeleton_material->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - - //position 3D Shared mesh - - pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); - { - - PoolVector<Vector3> cursor_points; - PoolVector<Color> cursor_colors; - float cs = 0.25; - cursor_points.push_back(Vector3(+cs, 0, 0)); - cursor_points.push_back(Vector3(-cs, 0, 0)); - cursor_points.push_back(Vector3(0, +cs, 0)); - cursor_points.push_back(Vector3(0, -cs, 0)); - cursor_points.push_back(Vector3(0, 0, +cs)); - cursor_points.push_back(Vector3(0, 0, -cs)); - cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); - - Ref<SpatialMaterial> mat = memnew(SpatialMaterial); - mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_line_width(3); - Array d; - d.resize(VS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - pos3d_mesh->surface_set_material(0, mat); - } - - listener_line_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); - { - - PoolVector<Vector3> cursor_points; - PoolVector<Color> cursor_colors; - cursor_points.push_back(Vector3(0, 0, 0)); - cursor_points.push_back(Vector3(0, 0, -1.0)); - cursor_colors.push_back(Color(0.5, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 0.5, 0.7)); - - Ref<SpatialMaterial> mat = memnew(SpatialMaterial); - mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_line_width(3); - Array d; - d.resize(VS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - listener_line_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - listener_line_mesh->surface_set_material(0, mat); - } - - room_material = create_line_material(Color(1.0, 0.6, 0.9)); - portal_material = create_line_material(Color(1.0, 0.8, 0.6)); - raycast_material = create_line_material(Color(1.0, 0.8, 0.6)); - car_wheel_material = create_line_material(Color(0.6, 0.8, 1.0)); - visibility_notifier_material = create_line_material(Color(1.0, 0.5, 1.0)); - particles_material = create_line_material(Color(1.0, 1.0, 0.5)); - reflection_probe_material = create_line_material(Color(0.5, 1.0, 0.7)); - reflection_probe_material_internal = create_line_material(Color(0.3, 0.8, 0.5, 0.15)); - gi_probe_material = create_line_material(Color(0.7, 1.0, 0.5)); - gi_probe_material_internal = create_line_material(Color(0.5, 0.8, 0.3, 0.1)); - joint_material = create_line_material(Color(0.6, 0.8, 1.0)); - - stream_player_icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - stream_player_icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - stream_player_icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - stream_player_icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - stream_player_icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - stream_player_icon->set_albedo(Color(1, 1, 1, 0.9)); - stream_player_icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, SpatialEditor::get_singleton()->get_icon("GizmoSpatialStreamPlayer", "EditorIcons")); - - visibility_notifier_icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - visibility_notifier_icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - visibility_notifier_icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - visibility_notifier_icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - visibility_notifier_icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - visibility_notifier_icon->set_albedo(Color(1, 1, 1, 0.9)); - visibility_notifier_icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, SpatialEditor::get_singleton()->get_icon("Visible", "EditorIcons")); - - listener_icon = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - listener_icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - listener_icon->set_cull_mode(SpatialMaterial::CULL_DISABLED); - listener_icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED); - listener_icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - listener_icon->set_albedo(Color(1, 1, 1, 0.9)); - listener_icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, SpatialEditor::get_singleton()->get_icon("GizmoListener", "EditorIcons")); - - { - - PoolVector<Vector3> vertices; - -#undef ADD_VTX -#define ADD_VTX(m_idx) \ - vertices.push_back(face_points[m_idx]); - - for (int i = 0; i < 6; i++) { - - Vector3 face_points[4]; - - for (int j = 0; j < 4; j++) { - - float v[3]; - v[0] = 1.0; - v[1] = 1 - 2 * ((j >> 1) & 1); - v[2] = v[1] * (1 - 2 * (j & 1)); - - for (int k = 0; k < 3; k++) { - - if (i < 3) - face_points[j][(i + k) % 3] = v[k]; - else - face_points[3 - j][(i + k) % 3] = -v[k]; - } - } - //tri 1 - ADD_VTX(0); - ADD_VTX(1); - ADD_VTX(2); - //tri 2 - ADD_VTX(2); - ADD_VTX(3); - ADD_VTX(0); - } - - test_cube_tm = Ref<TriangleMesh>(memnew(TriangleMesh)); - test_cube_tm->create(vertices); - } - - shape_material = create_line_material(Color(0.2, 1, 1.0)); -#endif - - pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); - { - - PoolVector<Vector3> cursor_points; - PoolVector<Color> cursor_colors; - float cs = 0.25; - cursor_points.push_back(Vector3(+cs, 0, 0)); - cursor_points.push_back(Vector3(-cs, 0, 0)); - cursor_points.push_back(Vector3(0, +cs, 0)); - cursor_points.push_back(Vector3(0, -cs, 0)); - cursor_points.push_back(Vector3(0, 0, +cs)); - cursor_points.push_back(Vector3(0, 0, -cs)); - cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(1, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 1, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 1, 0.7)); - - Ref<SpatialMaterial> mat = memnew(SpatialMaterial); - mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_line_width(3); - Array d; - d.resize(VS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - pos3d_mesh->surface_set_material(0, mat); - } - - listener_line_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); - { - - PoolVector<Vector3> cursor_points; - PoolVector<Color> cursor_colors; - cursor_points.push_back(Vector3(0, 0, 0)); - cursor_points.push_back(Vector3(0, 0, -1.0)); - cursor_colors.push_back(Color(0.5, 0.5, 0.5, 0.7)); - cursor_colors.push_back(Color(0.5, 0.5, 0.5, 0.7)); - - Ref<SpatialMaterial> mat = memnew(SpatialMaterial); - mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - mat->set_line_width(3); - Array d; - d.resize(VS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - listener_line_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - listener_line_mesh->surface_set_material(0, mat); - } -} diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 924f82dc16..877590b91d 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -55,187 +55,120 @@ class Camera; -class EditorSpatialGizmo : public SpatialEditorGizmo { +class LightSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(EditorSpatialGizmo, SpatialGizmo); + GDCLASS(LightSpatialGizmoPlugin, EditorSpatialGizmoPlugin); - struct Instance { - - RID instance; - Ref<ArrayMesh> mesh; - RID skeleton; - bool billboard; - bool unscaled; - bool can_intersect; - bool extra_margin; - Instance() { - - billboard = false; - unscaled = false; - can_intersect = false; - extra_margin = false; - } - - void create_instance(Spatial *p_base); - }; - - Vector<Vector3> collision_segments; - Ref<TriangleMesh> collision_mesh; - - struct Handle { - Vector3 pos; - bool billboard; - }; - - Vector<Vector3> handles; - Vector<Vector3> secondary_handles; - float selectable_icon_size = -1.0f; - bool billboard_handle; +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; - bool valid; - Spatial *base; - Vector<Instance> instances; - Spatial *spatial_node; + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorSpatialGizmo *p_gizmo); - void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to<Spatial>(p_node)); } + LightSpatialGizmoPlugin(); +}; -protected: - void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false); - void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID()); - void add_collision_segments(const Vector<Vector3> &p_lines); - void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); - void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1); - void add_handles(const Vector<Vector3> &p_handles, bool p_billboard = false, bool p_secondary = false); - void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3()); +class AudioStreamPlayer3DSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - void set_spatial_node(Spatial *p_node); - const Spatial *get_spatial_node() const { return spatial_node; } + GDCLASS(AudioStreamPlayer3DSpatialGizmoPlugin, EditorSpatialGizmoPlugin); - static void _bind_methods(); +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; - Ref<SpatialMaterial> create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); - Ref<SpatialMaterial> create_icon_material(const String &p_name, const Ref<Texture> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorSpatialGizmo *p_gizmo); -public: - virtual Vector3 get_handle_pos(int p_idx) const; - virtual bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum); - virtual bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); - - void clear(); - void create(); - void transform(); - virtual void redraw(); - void free(); - virtual bool is_editable() const; - virtual bool can_draw() const; - - EditorSpatialGizmo(); - ~EditorSpatialGizmo(); + AudioStreamPlayer3DSpatialGizmoPlugin(); }; -class LightSpatialGizmo : public EditorSpatialGizmo { +class CameraSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(LightSpatialGizmo, EditorSpatialGizmo); - - Light *light; + GDCLASS(CameraSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; - void redraw(); - LightSpatialGizmo(Light *p_light = NULL); -}; + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(EditorSpatialGizmo *p_gizmo); -class AudioStreamPlayer3DSpatialGizmo : public EditorSpatialGizmo { + CameraSpatialGizmoPlugin(); +}; - GDCLASS(AudioStreamPlayer3DSpatialGizmo, EditorSpatialGizmo); +class MeshInstanceSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - AudioStreamPlayer3D *player; + GDCLASS(MeshInstanceSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + bool can_be_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - AudioStreamPlayer3DSpatialGizmo(AudioStreamPlayer3D *p_player = NULL); + MeshInstanceSpatialGizmoPlugin(); }; -class CameraSpatialGizmo : public EditorSpatialGizmo { +class Sprite3DSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(CameraSpatialGizmo, EditorSpatialGizmo); - - Camera *camera; + GDCLASS(Sprite3DSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + bool can_be_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - CameraSpatialGizmo(Camera *p_camera = NULL); + Sprite3DSpatialGizmoPlugin(); }; -class MeshInstanceSpatialGizmo : public EditorSpatialGizmo { +class Position3DSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(MeshInstanceSpatialGizmo, EditorSpatialGizmo); + GDCLASS(Position3DSpatialGizmoPlugin, EditorSpatialGizmoPlugin); - MeshInstance *mesh; + Ref<ArrayMesh> pos3d_mesh; + Vector<Vector3> cursor_points; public: - virtual bool can_draw() const; - void redraw(); - MeshInstanceSpatialGizmo(MeshInstance *p_mesh = NULL); -}; + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); -class Sprite3DSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(Sprite3DSpatialGizmo, EditorSpatialGizmo); - - SpriteBase3D *sprite; - -public: - virtual bool can_draw() const; - void redraw(); - Sprite3DSpatialGizmo(SpriteBase3D *p_sprite = NULL); + Position3DSpatialGizmoPlugin(); }; -class Position3DSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(Position3DSpatialGizmo, EditorSpatialGizmo); +class SkeletonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - Position3D *p3d; + GDCLASS(SkeletonSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - void redraw(); - Position3DSpatialGizmo(Position3D *p_p3d = NULL); -}; - -class SkeletonSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(SkeletonSpatialGizmo, EditorSpatialGizmo); - - Skeleton *skel; + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); -public: - void redraw(); - SkeletonSpatialGizmo(Skeleton *p_skel = NULL); + SkeletonSpatialGizmoPlugin(); }; -class PhysicalBoneSpatialGizmo : public EditorSpatialGizmo { - GDCLASS(PhysicalBoneSpatialGizmo, EditorSpatialGizmo); +class PhysicalBoneSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - PhysicalBone *physical_bone; + GDCLASS(PhysicalBoneSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - //virtual Transform get_global_gizmo_transform(); - virtual void redraw(); - PhysicalBoneSpatialGizmo(PhysicalBone *p_pb = NULL); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + PhysicalBoneSpatialGizmoPlugin(); }; #if 0 @@ -251,137 +184,166 @@ public: }; #endif -class VisibilityNotifierGizmo : public EditorSpatialGizmo { +class RayCastSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(VisibilityNotifierGizmo, EditorSpatialGizmo); - - VisibilityNotifier *notifier; + GDCLASS(RayCastSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - VisibilityNotifierGizmo(VisibilityNotifier *p_notifier = NULL); + RayCastSpatialGizmoPlugin(); }; -class ParticlesGizmo : public EditorSpatialGizmo { +class VehicleWheelSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(ParticlesGizmo, EditorSpatialGizmo); - - Particles *particles; + GDCLASS(VehicleWheelSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - ParticlesGizmo(Particles *p_particles = NULL); + VehicleWheelSpatialGizmoPlugin(); }; -class ReflectionProbeGizmo : public EditorSpatialGizmo { +class SoftBodySpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(ReflectionProbeGizmo, EditorSpatialGizmo); - - ReflectionProbe *probe; + GDCLASS(SoftBodySpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + bool is_selectable_when_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - ReflectionProbeGizmo(ReflectionProbe *p_probe = NULL); -}; + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel); + bool is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int idx) const; -class GIProbeGizmo : public EditorSpatialGizmo { + SoftBodySpatialGizmoPlugin(); +}; - GDCLASS(GIProbeGizmo, EditorSpatialGizmo); +class VisibilityNotifierGizmoPlugin : public EditorSpatialGizmoPlugin { - GIProbe *probe; + GDCLASS(VisibilityNotifierGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - GIProbeGizmo(GIProbe *p_probe = NULL); -}; + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); -class BakedIndirectLightGizmo : public EditorSpatialGizmo { + VisibilityNotifierGizmoPlugin(); +}; - GDCLASS(BakedIndirectLightGizmo, EditorSpatialGizmo); +class ParticlesGizmoPlugin : public EditorSpatialGizmoPlugin { - BakedLightmap *baker; + GDCLASS(ParticlesGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + bool is_selectable_when_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); - void redraw(); - BakedIndirectLightGizmo(BakedLightmap *p_baker = NULL); -}; + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); -class CollisionShapeSpatialGizmo : public EditorSpatialGizmo { + ParticlesGizmoPlugin(); +}; - GDCLASS(CollisionShapeSpatialGizmo, EditorSpatialGizmo); +class ReflectionProbeGizmoPlugin : public EditorSpatialGizmoPlugin { - CollisionShape *cs; + GDCLASS(ReflectionProbeGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(); - CollisionShapeSpatialGizmo(CollisionShape *p_cs = NULL); -}; + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); -class CollisionPolygonSpatialGizmo : public EditorSpatialGizmo { + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + ReflectionProbeGizmoPlugin(); +}; - GDCLASS(CollisionPolygonSpatialGizmo, EditorSpatialGizmo); +class GIProbeGizmoPlugin : public EditorSpatialGizmoPlugin { - CollisionPolygon *polygon; + GDCLASS(GIProbeGizmoPlugin, EditorSpatialGizmoPlugin); public: - void redraw(); - CollisionPolygonSpatialGizmo(CollisionPolygon *p_polygon = NULL); -}; + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); -class RayCastSpatialGizmo : public EditorSpatialGizmo { + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - GDCLASS(RayCastSpatialGizmo, EditorSpatialGizmo); + GIProbeGizmoPlugin(); +}; - RayCast *raycast; +class BakedIndirectLightGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(BakedIndirectLightGizmoPlugin, EditorSpatialGizmoPlugin); public: - void redraw(); - RayCastSpatialGizmo(RayCast *p_raycast = NULL); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + BakedIndirectLightGizmoPlugin(); }; -class VehicleWheelSpatialGizmo : public EditorSpatialGizmo { +class CollisionShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(CollisionShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); + + CollisionShapeSpatialGizmoPlugin(); +}; - GDCLASS(VehicleWheelSpatialGizmo, EditorSpatialGizmo); +class CollisionPolygonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - VehicleWheel *car_wheel; + GDCLASS(CollisionPolygonSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - void redraw(); - VehicleWheelSpatialGizmo(VehicleWheel *p_car_wheel = NULL); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + CollisionPolygonSpatialGizmoPlugin(); }; -class NavigationMeshSpatialGizmo : public EditorSpatialGizmo { +class NavigationMeshSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(NavigationMeshSpatialGizmo, EditorSpatialGizmo); + GDCLASS(NavigationMeshSpatialGizmoPlugin, EditorSpatialGizmoPlugin); struct _EdgeKey { @@ -391,11 +353,12 @@ class NavigationMeshSpatialGizmo : public EditorSpatialGizmo { bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } }; - NavigationMeshInstance *navmesh; - public: - void redraw(); - NavigationMeshSpatialGizmo(NavigationMeshInstance *p_navmesh = NULL); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + NavigationMeshSpatialGizmoPlugin(); }; class JointGizmosDrawer { @@ -404,7 +367,7 @@ public: static Basis look_body_toward(Vector3::Axis p_axis, const Transform &joint_transform, const Transform &body_transform); static Basis look_body_toward_x(const Transform &p_joint_transform, const Transform &p_body_transform); static Basis look_body_toward_y(const Transform &p_joint_transform, const Transform &p_body_transform); - /// Special function just used for physics joints, it that returns a basis constrained toward Joint Z axis + /// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis /// with axis X and Y that are looking toward the body and oriented toward up static Basis look_body_toward_z(const Transform &p_joint_transform, const Transform &p_body_transform); @@ -413,66 +376,20 @@ public: static void draw_cone(const Transform &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points); }; -class PinJointSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(PinJointSpatialGizmo, EditorSpatialGizmo); - - PinJoint *p3d; - -public: - static void CreateGizmo(const Transform &p_offset, Vector<Vector3> &r_cursor_points); - - void redraw(); - PinJointSpatialGizmo(PinJoint *p_p3d = NULL); -}; - -class HingeJointSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(HingeJointSpatialGizmo, EditorSpatialGizmo); - - HingeJoint *p3d; - -public: - static void CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); - - void redraw(); - HingeJointSpatialGizmo(HingeJoint *p_p3d = NULL); -}; - -class SliderJointSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(SliderJointSpatialGizmo, EditorSpatialGizmo); - - SliderJoint *p3d; - -public: - static void CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); - - void redraw(); - SliderJointSpatialGizmo(SliderJoint *p_p3d = NULL); -}; - -class ConeTwistJointSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(ConeTwistJointSpatialGizmo, EditorSpatialGizmo); - - ConeTwistJoint *p3d; - -public: - static void CreateGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); - - void redraw(); - ConeTwistJointSpatialGizmo(ConeTwistJoint *p_p3d = NULL); -}; - -class Generic6DOFJointSpatialGizmo : public EditorSpatialGizmo { - - GDCLASS(Generic6DOFJointSpatialGizmo, EditorSpatialGizmo); +class JointSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - Generic6DOFJoint *p3d; + GDCLASS(JointSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - static void CreateGizmo( + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + static void CreatePinJointGizmo(const Transform &p_offset, Vector<Vector3> &r_cursor_points); + static void CreateHingeJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector<Vector3> &r_common_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); + static void CreateSliderJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector<Vector3> &r_points, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); + static void CreateConeTwistJointGizmo(const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, const Transform &p_trs_body_b, real_t p_swing, real_t p_twist, Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); + static void CreateGeneric6DOFJointGizmo( const Transform &p_offset, const Transform &p_trs_joint, const Transform &p_trs_body_a, @@ -499,26 +416,7 @@ public: Vector<Vector3> *r_body_a_points, Vector<Vector3> *r_body_b_points); - void redraw(); - Generic6DOFJointSpatialGizmo(Generic6DOFJoint *p_p3d = NULL); + JointSpatialGizmoPlugin(); }; -class SpatialEditorGizmos { - -public: - HashMap<String, Ref<SpatialMaterial> > material_cache; - - Ref<SpatialMaterial> handle2_material; - Ref<SpatialMaterial> handle2_material_billboard; - Ref<SpatialMaterial> handle_material; - Ref<SpatialMaterial> handle_material_billboard; - Ref<Texture> handle_t; - Ref<ArrayMesh> pos3d_mesh; - Ref<ArrayMesh> listener_line_mesh; - static SpatialEditorGizmos *singleton; - - Ref<SpatialEditorGizmo> get_gizmo(Spatial *p_spatial); - - SpatialEditorGizmos(); -}; #endif // SPATIAL_EDITOR_GIZMOS_H diff --git a/editor/translations/af.po b/editor/translations/af.po index c5853bbb2f..d4c8bc26f4 100644 --- a/editor/translations/af.po +++ b/editor/translations/af.po @@ -7328,6 +7328,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ar.po b/editor/translations/ar.po index ccf2b97d9a..1535ccafb6 100644 --- a/editor/translations/ar.po +++ b/editor/translations/ar.po @@ -2,7 +2,6 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # Adel <dragonhunter250@gmail.com>, 2018. # athomield <athomield@hotmail.com>, 2017. # Basil Al-Khateeb <basil.y.alkhateeb@gmail.com>, 2017. @@ -15,13 +14,12 @@ # omar anwar aglan <omar.aglan91@yahoo.com>, 2017-2018. # OWs Tetra <owstetra@gmail.com>, 2017. # Rached Noureddine <rached.noureddine@gmail.com>, 2018. -# Rex_sa <asd1234567890m@gmail.com>, 2017. +# Rex_sa <asd1234567890m@gmail.com>, 2017, 2018. # Wajdi Feki <wajdi.feki@gmail.com>, 2017. -# msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-05-28 18:34+0000\n" +"PO-Revision-Date: 2018-07-15 19:34+0000\n" "Last-Translator: Rached Noureddine <rached.noureddine@gmail.com>\n" "Language-Team: Arabic <https://hosted.weblate.org/projects/godot-engine/" "godot/ar/>\n" @@ -30,7 +28,7 @@ msgstr "" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -4331,11 +4329,11 @@ msgstr "ولد رؤية AABB" #: editor/plugins/path_2d_editor_plugin.cpp msgid "Remove Point from Curve" -msgstr "" +msgstr "إزالة نقطة من المنحنى" #: editor/plugins/path_2d_editor_plugin.cpp msgid "Remove Out-Control from Curve" -msgstr "" +msgstr "أزل Out-Control من المنحنى" #: editor/plugins/path_2d_editor_plugin.cpp msgid "Remove In-Control from Curve" @@ -7392,6 +7390,11 @@ msgstr "بناء المشروع" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "إظهار الملفات" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" @@ -8076,7 +8079,7 @@ msgstr "" #: scene/resources/dynamic_font.cpp msgid "Invalid font size." -msgstr "" +msgstr "حجم الخط غير صالح" #~ msgid "Next" #~ msgstr "التالي" diff --git a/editor/translations/bg.po b/editor/translations/bg.po index 9f366b3d2f..71b9216b46 100644 --- a/editor/translations/bg.po +++ b/editor/translations/bg.po @@ -7383,6 +7383,11 @@ msgstr "Проект" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Преглед на файловете" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/bn.po b/editor/translations/bn.po index 3d00e3450c..aa36beefb6 100644 --- a/editor/translations/bn.po +++ b/editor/translations/bn.po @@ -7773,6 +7773,11 @@ msgstr "নতুন প্রকল্প" msgid "Warnings" msgstr "সতর্কতা" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "ফাইল" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ca.po b/editor/translations/ca.po index d2bffb0f84..085241296e 100644 --- a/editor/translations/ca.po +++ b/editor/translations/ca.po @@ -7438,6 +7438,11 @@ msgstr "Munta el Projecte" msgid "Warnings" msgstr "Avisos" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Visualitza Fitxers" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Final de la traça de la pila d'excepció interna" diff --git a/editor/translations/cs.po b/editor/translations/cs.po index 1066bbad94..b4cf176796 100644 --- a/editor/translations/cs.po +++ b/editor/translations/cs.po @@ -7372,6 +7372,11 @@ msgstr "Sestavit projekt" msgid "Warnings" msgstr "Varování" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Zobrazit soubory" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/da.po b/editor/translations/da.po index 3b5854334a..fcfdb6d249 100644 --- a/editor/translations/da.po +++ b/editor/translations/da.po @@ -7445,6 +7445,11 @@ msgstr "Projekt" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Vis filer" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/de.po b/editor/translations/de.po index d5d63f654b..c61d78e37e 100644 --- a/editor/translations/de.po +++ b/editor/translations/de.po @@ -27,12 +27,15 @@ # Tim Schellenberg <smwleod@gmail.com>, 2017. # Timo Schwarzer <account@timoschwarzer.com>, 2016-2018. # viernullvier <hannes.breul+github@gmail.com>, 2016. +# Arndt Heuvel <codeforpb@schatzkarten.net>, 2018. +# Gordon <gkone@gmx.net>, 2018. +# chillhelm <wilhelm@neubert.online>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-06-19 19:38+0000\n" -"Last-Translator: nimradium <nimra242001@gmail.com>\n" +"PO-Revision-Date: 2018-07-05 12:38+0000\n" +"Last-Translator: So Wieso <sowieso@dukun.de>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/" "godot/de/>\n" "Language: de\n" @@ -40,7 +43,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0.1\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -331,8 +334,7 @@ msgstr "Optimieren" #: editor/animation_editor.cpp msgid "Select an AnimationPlayer from the Scene Tree to edit animations." msgstr "" -"Wählen Sie einen AnimationPlayer aus dem Szenenbaum aus, um Animationen zu " -"bearbeiten." +"AnimationPlayer aus dem Szenenbaum auswählen um Animationen zu bearbeiten." #: editor/animation_editor.cpp msgid "Key" @@ -1948,7 +1950,7 @@ msgstr "Umwandeln zu..." #: editor/editor_node.cpp msgid "MeshLibrary..." -msgstr "MeshLibrary..." +msgstr "Mesh-Bibliothek..." #: editor/editor_node.cpp msgid "TileSet..." @@ -2483,7 +2485,7 @@ msgstr "Mirrors werden geladen, bitte warten..." #: editor/export_template_manager.cpp msgid "Remove template version '%s'?" -msgstr "Template-Version '%s' entfernen?" +msgstr "Template-Version ‚%s‘ entfernen?" #: editor/export_template_manager.cpp msgid "Can't open export templates zip." @@ -6048,8 +6050,8 @@ msgid "" "Couldn't load project.godot in project path (error %d). It may be missing or " "corrupted." msgstr "" -"Konnte project.godot im Projektpfad nicht laden (Fehler %d). Sie könnte " -"fehlen oder beschädigt worden sein." +"Die Datei project.godot im Projektpfad konnte nicht geladen werden (Fehler " +"%d). Sie könnte fehlen oder beschädigt sein." #: editor/project_manager.cpp msgid "Couldn't edit project.godot in project path." @@ -7490,6 +7492,11 @@ msgstr "Projekt bauen" msgid "Warnings" msgstr "Warnungen" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Dateien anzeigen" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Ende des inneren Exception-Stack-Traces" diff --git a/editor/translations/de_CH.po b/editor/translations/de_CH.po index 26f824bc4b..7d11a9d09f 100644 --- a/editor/translations/de_CH.po +++ b/editor/translations/de_CH.po @@ -7424,6 +7424,11 @@ msgstr "Projektname:" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Datei(en) öffnen" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index 1cb31e0ee9..d0f1d04a10 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -7265,6 +7265,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/el.po b/editor/translations/el.po index b3275b4647..4a9560e602 100644 --- a/editor/translations/el.po +++ b/editor/translations/el.po @@ -2,13 +2,11 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # George Tsiamasiotis <gtsiam@windowslive.com>, 2017-2018. -# msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-05-20 09:37+0000\n" +"PO-Revision-Date: 2018-07-21 22:38+0000\n" "Last-Translator: George Tsiamasiotis <gtsiam@windowslive.com>\n" "Language-Team: Greek <https://hosted.weblate.org/projects/godot-engine/godot/" "el/>\n" @@ -16,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -1135,7 +1133,7 @@ msgstr "Εμφάνιση στη διαχείριση αρχείων" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "New Folder..." -msgstr "Νέος φάκελος" +msgstr "Νέος φάκελος..." #: editor/editor_file_dialog.cpp msgid "Refresh" @@ -2698,7 +2696,7 @@ msgstr "Μετονομασία..." #: editor/filesystem_dock.cpp msgid "Move To..." -msgstr "Μετακίνηση σε" +msgstr "Μετακίνηση σε..." #: editor/filesystem_dock.cpp msgid "Open Scene(s)" @@ -2710,15 +2708,15 @@ msgstr "Στιγμιότυπο" #: editor/filesystem_dock.cpp msgid "Edit Dependencies..." -msgstr "Επεξεργασία εξαρτήσεων" +msgstr "Επεξεργασία εξαρτήσεων..." #: editor/filesystem_dock.cpp msgid "View Owners..." -msgstr "Προβολή ιδιοκτητών" +msgstr "Προβολή ιδιοκτητών..." #: editor/filesystem_dock.cpp msgid "Duplicate..." -msgstr "Αναπαραγωγή" +msgstr "Αναπαραγωγή..." #: editor/filesystem_dock.cpp msgid "Previous Directory" @@ -4031,7 +4029,7 @@ msgstr "Δημιουργία περιγράμματος" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "Πλέγμα" +msgstr "Πλέγμα..." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -5712,9 +5710,8 @@ msgid "Options" msgstr "Επιλογές" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Has,Many,Options" -msgstr "Έχει,Πάρα,Πολλές,Επιλογές!" +msgstr "Έχει,Πολλές,Επιλογές" #: editor/plugins/theme_editor_plugin.cpp msgid "Tab 1" @@ -6001,9 +5998,8 @@ msgid "Imported Project" msgstr "Εισαγμένο έργο" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid Project Name." -msgstr "Όνομα έργου:" +msgstr "Άκυρο όνομα έργου." #: editor/project_manager.cpp msgid "Couldn't create folder." @@ -6207,13 +6203,12 @@ msgid "Mouse Button" msgstr "Κουμπί ποντικιού" #: editor/project_settings_editor.cpp -#, fuzzy msgid "" "Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or " "'\"'." msgstr "" "Άκυρο όνομα ενέργειας. Δεν μπορεί να είναι άδειο ή να περιέχει '/', ':', " -"'=', '\\' ή '\"'" +"'=', '\\' ή '\"'." #: editor/project_settings_editor.cpp msgid "Action '%s' already exists!" @@ -7474,6 +7469,11 @@ msgstr "Δόμηση έργου" msgid "Warnings" msgstr "Προειδοποιήσεις" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Προβολή αρχείων" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Τέλος ιχνηλάτησης στοίβας εσωτερικής εξαίρεσης" diff --git a/editor/translations/es.po b/editor/translations/es.po index 89118d2501..8e1f0b13c9 100644 --- a/editor/translations/es.po +++ b/editor/translations/es.po @@ -29,12 +29,13 @@ # Swyter <swyterzone@gmail.com>, 2016-2017. # Vazquinhos <vazquinhos@gmail.com>, 2018. # Yovani Damián <blackblex@gmail.com>, 2018. +# Andrus Diaz German <andrusdiazaleman@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-06-22 08:31+0000\n" -"Last-Translator: R. Joshua Seville <rjoshua@protonmail.com>\n" +"PO-Revision-Date: 2018-07-20 16:43+0000\n" +"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot/es/>\n" "Language: es\n" @@ -54,19 +55,19 @@ msgstr "Toda la Selección" #: editor/animation_editor.cpp msgid "Anim Change Keyframe Time" -msgstr "Cambiar el tiempo del Fotograma Clave de Animación" +msgstr "Cambiar el Tiempo del Fotograma Clave de Animación" #: editor/animation_editor.cpp msgid "Anim Change Transition" -msgstr "Cambiar Transición de Animación" +msgstr "Cambiar la Transición de Animación" #: editor/animation_editor.cpp msgid "Anim Change Transform" -msgstr "Cambiar transformación de animación" +msgstr "Cambiar la Transformación de la Animación" #: editor/animation_editor.cpp msgid "Anim Change Keyframe Value" -msgstr "Cambiar valor del Fotograma Clave de Animación" +msgstr "Cambiar valor de la Clave de Animación" #: editor/animation_editor.cpp msgid "Anim Change Call" @@ -114,7 +115,7 @@ msgstr "Cambiar Modo de Ciclo de Pista de Animación" #: editor/animation_editor.cpp msgid "Edit Node Curve" -msgstr "Editar Nodo de Curva" +msgstr "Editar Curva del Nodo" #: editor/animation_editor.cpp msgid "Edit Selection Curve" @@ -163,7 +164,7 @@ msgstr "Escalar Selección" #: editor/animation_editor.cpp msgid "Scale From Cursor" -msgstr "Escalar desde cursor" +msgstr "Escalar Desde Cursor" #: editor/animation_editor.cpp msgid "Goto Next Step" @@ -212,11 +213,11 @@ msgstr "Limpiar Animación" #: editor/animation_editor.cpp msgid "Create NEW track for %s and insert key?" -msgstr "¿Quieres crear una NUEVA pista para %s e insertar clave?" +msgstr "¿Crear NUEVA pista para %s e insertar clave?" #: editor/animation_editor.cpp msgid "Create %d NEW tracks and insert keys?" -msgstr "¿Quieres crear %d NUEVAS pistas e insertar claves?" +msgstr "¿Crear %d NUEVAS pistas e insertar claves?" #: editor/animation_editor.cpp editor/create_dialog.cpp #: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp @@ -288,7 +289,7 @@ msgstr "Activar/Desactivar Bucle de Animación." #: editor/animation_editor.cpp msgid "Add new tracks." -msgstr "Añadir Nuevas Pistas." +msgstr "Añadir nuevas pistas." #: editor/animation_editor.cpp msgid "Move current track up." @@ -308,7 +309,7 @@ msgstr "Herramientas de pistas" #: editor/animation_editor.cpp msgid "Enable editing of individual keys by clicking them." -msgstr "Habilitar la edición de claves individuales al hacer clic." +msgstr "Habilitar la edición de claves individuales haciendo clic en ellas." #: editor/animation_editor.cpp msgid "Anim. Optimizer" @@ -366,7 +367,7 @@ msgstr "Limpiar todas las animaciones" #: editor/animation_editor.cpp msgid "Clean-Up Animation(s) (NO UNDO!)" -msgstr "Limpiar todas las animaciones (IRREVERSIBLE)" +msgstr "Limpiar las Animación(es) (¡IRREVERSIBLE!)" #: editor/animation_editor.cpp msgid "Clean-Up" @@ -374,39 +375,39 @@ msgstr "Limpiar" #: editor/array_property_edit.cpp msgid "Resize Array" -msgstr "Redimensionar «array»" +msgstr "Redimensionar Array" #: editor/array_property_edit.cpp msgid "Change Array Value Type" -msgstr "Cambiar tipo de valor del «array»" +msgstr "Cambiar Tipo de Valor del Array" #: editor/array_property_edit.cpp msgid "Change Array Value" -msgstr "Cambiar valor del «array»" +msgstr "Cambiar Valor del Array" #: editor/code_editor.cpp msgid "Go to Line" -msgstr "Ir a línea" +msgstr "Ir a Línea" #: editor/code_editor.cpp msgid "Line Number:" -msgstr "Número de línea:" +msgstr "Número de Línea:" #: editor/code_editor.cpp msgid "No Matches" -msgstr "Sin coincidencias" +msgstr "Sin Coincidencias" #: editor/code_editor.cpp msgid "Replaced %d occurrence(s)." -msgstr "%d ocurrencia/s reemplazadas." +msgstr "%d Ocurrencia(s) Reemplazada(s)." #: editor/code_editor.cpp msgid "Match Case" -msgstr "Coincidir mayús/minúsculas" +msgstr "Coincidir Mayús/Minúsculas" #: editor/code_editor.cpp msgid "Whole Words" -msgstr "Palabras completas" +msgstr "Palabras Completas" #: editor/code_editor.cpp msgid "Replace" @@ -414,11 +415,11 @@ msgstr "Reemplazar" #: editor/code_editor.cpp msgid "Replace All" -msgstr "Reemplazar todo" +msgstr "Reemplazar Todo" #: editor/code_editor.cpp msgid "Selection Only" -msgstr "Sólo selección" +msgstr "Sólo Selección" #: editor/code_editor.cpp msgid "Zoom In" @@ -430,7 +431,7 @@ msgstr "Alejar" #: editor/code_editor.cpp msgid "Reset Zoom" -msgstr "Restablecer zoom" +msgstr "Restablecer Zoom" #: editor/code_editor.cpp editor/script_editor_debugger.cpp msgid "Line:" @@ -449,8 +450,8 @@ msgid "" "Target method not found! Specify a valid method or attach a script to target " "Node." msgstr "" -"No se encontró el método del objetivo! Especifica un método válido o adjunta " -"un script al Nodo objetivo." +"¡Método objetivo no encontrado! Especifica un método válido o añade un " +"script al Nodo objetivo." #: editor/connections_dialog.cpp msgid "Connect To Node:" @@ -471,11 +472,11 @@ msgstr "Quitar" #: editor/connections_dialog.cpp msgid "Add Extra Call Argument:" -msgstr "Añadir argumento extra de llamada:" +msgstr "Añadir Argumento Extra de Llamada:" #: editor/connections_dialog.cpp msgid "Extra Call Arguments:" -msgstr "Argumentos extras de llamada:" +msgstr "Argumentos Extras de Llamada:" #: editor/connections_dialog.cpp msgid "Path to Node:" @@ -483,7 +484,7 @@ msgstr "Ruta al Nodo:" #: editor/connections_dialog.cpp msgid "Make Function" -msgstr "Crear función" +msgstr "Crear Función" #: editor/connections_dialog.cpp msgid "Deferred" @@ -538,7 +539,7 @@ msgstr "Señales" #: editor/create_dialog.cpp msgid "Change %s Type" -msgstr "Cambiar el tipo de %s" +msgstr "Cambiar el Tipo de %s" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -579,11 +580,11 @@ msgstr "Descripción:" #: editor/dependency_editor.cpp msgid "Search Replacement For:" -msgstr "Buscar reemplazo para:" +msgstr "Buscar Reemplazo Para:" #: editor/dependency_editor.cpp msgid "Dependencies For:" -msgstr "Dependencias para:" +msgstr "Dependencias Para:" #: editor/dependency_editor.cpp msgid "" @@ -622,7 +623,7 @@ msgstr "Dependencias:" #: editor/dependency_editor.cpp msgid "Fix Broken" -msgstr "Arreglar rota(s)" +msgstr "Arreglar Rota(s)" #: editor/dependency_editor.cpp msgid "Dependency Editor" @@ -630,7 +631,7 @@ msgstr "Editor de Dependencias" #: editor/dependency_editor.cpp msgid "Search Replacement Resource:" -msgstr "Buscar recurso de reemplazo:" +msgstr "Buscar Recurso de Reemplazo:" #: editor/dependency_editor.cpp editor/editor_file_dialog.cpp #: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp @@ -641,13 +642,11 @@ msgstr "Abrir" #: editor/dependency_editor.cpp msgid "Owners Of:" -msgstr "Dueños de:" +msgstr "Dueños De:" #: editor/dependency_editor.cpp msgid "Remove selected files from the project? (no undo)" -msgstr "" -"¿Quieres quitar los archivos seleccionados del proyecto? (No puedes " -"deshacerlo)" +msgstr "¿Quitar los archivos seleccionados del proyecto? (irreversible)" #: editor/dependency_editor.cpp msgid "" @@ -657,7 +656,7 @@ msgid "" msgstr "" "Otros recursos necesitan los archivos que estás intentando quitar para " "funcionar.\n" -"¿Seguro que quieres quitarlos? (No puedes deshacerlo)" +"¿Quitarlos de todos modos? (irreversible)" #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -673,7 +672,7 @@ msgstr "La escena no se pudo cargar porque faltan las siguientes dependencias:" #: editor/dependency_editor.cpp editor/editor_node.cpp msgid "Open Anyway" -msgstr "Abrir de todos modos" +msgstr "Abrir de Todos Modos" #: editor/dependency_editor.cpp msgid "Which action should be taken?" @@ -689,23 +688,23 @@ msgstr "¡Hubo errores al cargar!" #: editor/dependency_editor.cpp msgid "Permanently delete %d item(s)? (No undo!)" -msgstr "¿Quieres eliminar permanentemente %d elementos? (Irreversible)" +msgstr "¿Eliminar permanentemente %d elemento(s)? (¡Irreversible!)" #: editor/dependency_editor.cpp msgid "Owns" -msgstr "Es dueño de" +msgstr "Dueño de" #: editor/dependency_editor.cpp msgid "Resources Without Explicit Ownership:" -msgstr "Recursos sin propietario explícito:" +msgstr "Recursos Sin Propietario Explícito:" #: editor/dependency_editor.cpp editor/editor_node.cpp msgid "Orphan Resource Explorer" -msgstr "Explorador de recursos huérfanos" +msgstr "Explorador de Recursos Huérfanos" #: editor/dependency_editor.cpp msgid "Delete selected files?" -msgstr "¿Quieres eliminar los archivos seleccionados?" +msgstr "¿Eliminar los archivos seleccionados?" #: editor/dependency_editor.cpp editor/editor_audio_buses.cpp #: editor/editor_file_dialog.cpp editor/editor_node.cpp @@ -745,7 +744,7 @@ msgstr "Desarrollador Principal" #: editor/editor_about.cpp msgid "Project Manager " -msgstr "Administrador de proyectos " +msgstr "Administrador de Proyectos " #: editor/editor_about.cpp msgid "Developers" @@ -757,11 +756,11 @@ msgstr "Autores" #: editor/editor_about.cpp msgid "Platinum Sponsors" -msgstr "Patrocinadores Platinum" +msgstr "Patrocinadores de Platino" #: editor/editor_about.cpp msgid "Gold Sponsors" -msgstr "Patrocinadores Gold" +msgstr "Patrocinadores de Oro" #: editor/editor_about.cpp msgid "Mini Sponsors" @@ -769,11 +768,11 @@ msgstr "Mini Patrocinadores" #: editor/editor_about.cpp msgid "Gold Donors" -msgstr "Donantes Gold" +msgstr "Donantes de Oro" #: editor/editor_about.cpp msgid "Silver Donors" -msgstr "Donantes Silver" +msgstr "Donantes de Plata" #: editor/editor_about.cpp msgid "Bronze Donors" @@ -825,7 +824,7 @@ msgstr "Descomprimiendo Assets" #: editor/editor_asset_installer.cpp editor/project_manager.cpp msgid "Package Installed Successfully!" -msgstr "¡El paquete se ha instalado correctamente!" +msgstr "¡Paquete Instalado Exitosamente!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -859,19 +858,19 @@ msgstr "Cambiar Volumen de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Solo" -msgstr "Act./Desact. Solo de Bus de Audio" +msgstr "Alternar Solo de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Mute" -msgstr "Alternar Mute del Bus de Audio" +msgstr "Alternar Silencio de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Toggle Audio Bus Bypass Effects" -msgstr "Alternar puenteado de efectos en Bus de Audio" +msgstr "Alternar Puenteado de Efectos de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Select Audio Bus Send" -msgstr "Seleccionar Envío de Audio Bus" +msgstr "Seleccionar Envío de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus Effect" @@ -928,7 +927,7 @@ msgstr "Añadir Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Master bus can't be deleted!" -msgstr "¡No se puede borrar el Bus Maestro!" +msgstr "¡No se puede borrar el bus maestro!" #: editor/editor_audio_buses.cpp msgid "Delete Audio Bus" @@ -940,7 +939,7 @@ msgstr "Duplicar Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Reset Bus Volume" -msgstr "Restablecer Volumen del Bus" +msgstr "Restablecer Volumen de Bus" #: editor/editor_audio_buses.cpp msgid "Move Audio Bus" @@ -948,7 +947,7 @@ msgstr "Mover Bus de Audio" #: editor/editor_audio_buses.cpp msgid "Save Audio Bus Layout As..." -msgstr "Guardar configuración de los Buses de Audio como..." +msgstr "Guardar Configuración de Bus de Audio Como..." #: editor/editor_audio_buses.cpp msgid "Location for New Layout..." @@ -956,7 +955,7 @@ msgstr "Ubicación para Nueva Configuración..." #: editor/editor_audio_buses.cpp msgid "Open Audio Bus Layout" -msgstr "Abrir configuración de Bus de Audio" +msgstr "Abrir Configuración de Bus de Audio" #: editor/editor_audio_buses.cpp msgid "There is no 'res://default_bus_layout.tres' file." @@ -1002,7 +1001,7 @@ msgstr "Cargar configuración de Bus por defecto." #: editor/editor_autoload_settings.cpp msgid "Invalid name." -msgstr "El nombre no es correcto." +msgstr "Nombre inválido." #: editor/editor_autoload_settings.cpp msgid "Valid characters:" @@ -1011,24 +1010,24 @@ msgstr "Letras válidas:" #: editor/editor_autoload_settings.cpp msgid "Invalid name. Must not collide with an existing engine class name." msgstr "" -"El nombre no es correcto. No puede coincidir con el nombre de una clase que " -"ya exista en el motor gráfico." +"Nombre inválido. No debe coincidir con el nombre de una clase que ya exista " +"en el motor gráfico." #: editor/editor_autoload_settings.cpp msgid "Invalid name. Must not collide with an existing buit-in type name." msgstr "" -"El nombre no es correcto. No puede coincidir con un nombre de tipo que ya " -"esté integrado en el motor gráfico." +"Nombre inválido. No debe coincidir con un nombre de tipo que ya esté " +"integrado en el motor gráfico." #: editor/editor_autoload_settings.cpp msgid "Invalid name. Must not collide with an existing global constant name." msgstr "" -"El nombre no es correcto. No puede coincidir con un nombre de constante " -"global ya existente en el motor gráfico." +"Nombre inválido. No debe coincidir con un nombre de constante global ya " +"existente en el motor gráfico." #: editor/editor_autoload_settings.cpp msgid "Invalid Path." -msgstr "Ruta incorrecta." +msgstr "Ruta Inválida." #: editor/editor_autoload_settings.cpp msgid "File does not exist." @@ -1052,7 +1051,7 @@ msgstr "Renombrar Autoload" #: editor/editor_autoload_settings.cpp msgid "Toggle AutoLoad Globals" -msgstr "Des/Activar Globales de Autoload" +msgstr "Alternar Globales de Autoload" #: editor/editor_autoload_settings.cpp msgid "Move Autoload" @@ -1106,7 +1105,7 @@ msgstr "[vacío]" #: editor/editor_data.cpp msgid "[unsaved]" -msgstr "[no guardado]" +msgstr "[sin guardar]" #: editor/editor_dir_dialog.cpp msgid "Please select a base directory first" @@ -1114,12 +1113,12 @@ msgstr "Por favor, selecciona primero un directorio base" #: editor/editor_dir_dialog.cpp msgid "Choose a Directory" -msgstr "Elige una carpeta" +msgstr "Elige un Directorio" #: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp msgid "Create Folder" -msgstr "Crear carpeta" +msgstr "Crear Carpeta" #: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp #: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp @@ -1139,7 +1138,7 @@ msgstr "Elegir" #: editor/editor_export.cpp msgid "Storing File:" -msgstr "Archivo de almacenamiento:" +msgstr "Archivo de Almacenamiento:" #: editor/editor_export.cpp msgid "Packing" @@ -1147,11 +1146,11 @@ msgstr "Empaquetando" #: editor/editor_export.cpp platform/javascript/export/export.cpp msgid "Template file not found:" -msgstr "No se encontró archivo de plantilla:" +msgstr "Archivo de plantilla no encontrado:" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "File Exists, Overwrite?" -msgstr "El archivo ya existe, ¿quieres sobreescribirlo?" +msgstr "El Archivo ya Existe, ¿Quieres Sobreescribirlo?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select Current Folder" @@ -1159,15 +1158,15 @@ msgstr "Seleccionar Carpeta Actual" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Copy Path" -msgstr "Copiar ruta" +msgstr "Copiar Ruta" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Show In File Manager" -msgstr "Mostrar en el navegador de archivos" +msgstr "Mostrar en el Navegador de Archivos" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "New Folder..." -msgstr "Nueva carpeta..." +msgstr "Nueva Carpeta..." #: editor/editor_file_dialog.cpp msgid "Refresh" @@ -1183,19 +1182,19 @@ msgstr "Todos los archivos (*)" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open a File" -msgstr "Abrir un archivo" +msgstr "Abrir un Archivo" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open File(s)" -msgstr "Abrir archivo/s" +msgstr "Abrir Archivo(s)" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open a Directory" -msgstr "Abrir una carpeta" +msgstr "Abrir un Directorio" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open a File or Directory" -msgstr "Abrir un archivo o carpeta" +msgstr "Abrir un Archivo o Directorio" #: editor/editor_file_dialog.cpp editor/editor_node.cpp #: editor/plugins/animation_player_editor_plugin.cpp @@ -1205,7 +1204,7 @@ msgstr "Guardar" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Save a File" -msgstr "Guardar un archivo" +msgstr "Guardar un Archivo" #: editor/editor_file_dialog.cpp msgid "Go Back" @@ -1221,27 +1220,27 @@ msgstr "Subir" #: editor/editor_file_dialog.cpp msgid "Toggle Hidden Files" -msgstr "Ver/ocultar archivos ocultos" +msgstr "Ver/ocultar Archivos Ocultos" #: editor/editor_file_dialog.cpp msgid "Toggle Favorite" -msgstr "Añadir/quitar favorito" +msgstr "Añadir/quitar Favorito" #: editor/editor_file_dialog.cpp msgid "Toggle Mode" -msgstr "Cambiar modo" +msgstr "Cambiar Modo" #: editor/editor_file_dialog.cpp msgid "Focus Path" -msgstr "Seleccionar ruta" +msgstr "Seleccionar Ruta" #: editor/editor_file_dialog.cpp msgid "Move Favorite Up" -msgstr "Subir favorito" +msgstr "Subir Favorito" #: editor/editor_file_dialog.cpp msgid "Move Favorite Down" -msgstr "Bajar favorito" +msgstr "Bajar Favorito" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Go to parent folder" @@ -1249,7 +1248,7 @@ msgstr "Ir a la carpeta principal" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Directories & Files:" -msgstr "Carpetas y archivos:" +msgstr "Directorios y Archivos:" #: editor/editor_file_dialog.cpp msgid "Preview:" @@ -1266,7 +1265,7 @@ msgstr "Debe tener una extensión válida." #: editor/editor_file_system.cpp msgid "ScanSources" -msgstr "Analizando fuentes" +msgstr "Analizando Fuentes" #: editor/editor_file_system.cpp msgid "(Re)Importing Assets" @@ -1275,15 +1274,15 @@ msgstr "(Re)Importando Assets" #: editor/editor_help.cpp editor/editor_node.cpp #: editor/plugins/script_editor_plugin.cpp msgid "Search Help" -msgstr "Ayuda de búsqueda" +msgstr "Ayuda de Búsqueda" #: editor/editor_help.cpp msgid "Class List:" -msgstr "Lista de clases:" +msgstr "Lista de Clases:" #: editor/editor_help.cpp msgid "Search Classes" -msgstr "Buscar clases" +msgstr "Buscar Clases" #: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp msgid "Top" @@ -1303,7 +1302,7 @@ msgstr "Heredada por:" #: editor/editor_help.cpp msgid "Brief Description:" -msgstr "Descripción breve:" +msgstr "Descripción Breve:" #: editor/editor_help.cpp msgid "Members" @@ -1323,11 +1322,11 @@ msgstr "Métodos públicos:" #: editor/editor_help.cpp msgid "GUI Theme Items" -msgstr "Elementos del Tema de GUI" +msgstr "Elementos del Tema de Interfaz" #: editor/editor_help.cpp msgid "GUI Theme Items:" -msgstr "Elementos de tema de interfaz:" +msgstr "Elementos del Tema de Interfaz:" #: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp msgid "Signals:" @@ -1359,7 +1358,7 @@ msgstr "Descripción" #: editor/editor_help.cpp msgid "Online Tutorials:" -msgstr "Tutoriales en línea:" +msgstr "Tutoriales en Línea:" #: editor/editor_help.cpp msgid "" @@ -1367,8 +1366,8 @@ msgid "" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" -"De momento no hay tutoriales para esta clase, puedes [color=$color][url=" -"$url]añadir uno[/url][/color] o [color=$color][url=$url2]pedir uno[color=" +"Actualmente no hay tutoriales para esta clase, puedes [color=$color][url=" +"$url]aportar uno[/url][/color] o [color=$color][url=$url2]pedir uno[color=" "$color][url=$url2]." #: editor/editor_help.cpp @@ -1393,7 +1392,7 @@ msgstr "Métodos" #: editor/editor_help.cpp msgid "Method Description:" -msgstr "Descripción de métodos:" +msgstr "Descripción del Método:" #: editor/editor_help.cpp msgid "" @@ -1405,11 +1404,11 @@ msgstr "" #: editor/editor_help.cpp msgid "Search Text" -msgstr "Texto de búsqueda" +msgstr "Texto de Búsqueda" #: editor/editor_help.cpp msgid "Find" -msgstr "Búsqueda" +msgstr "Buscar" #: editor/editor_log.cpp msgid "Output:" @@ -1420,11 +1419,11 @@ msgstr "Salida:" #: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp #: scene/gui/text_edit.cpp msgid "Clear" -msgstr "Borrar todo" +msgstr "Borrar Todo" #: editor/editor_log.cpp msgid "Clear Output" -msgstr "Borrar salida" +msgstr "Borrar Salida" #: editor/editor_node.cpp msgid "Project export failed with error code %d." @@ -1432,7 +1431,7 @@ msgstr "La exportación del proyecto falló con el código de error %d." #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" -msgstr "¡Hubo un error al guardar el recurso!" +msgstr "¡Error al guardar el recurso!" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Save Resource As..." @@ -1485,7 +1484,7 @@ msgstr "Analizando" #: editor/editor_node.cpp msgid "Creating Thumbnail" -msgstr "Creando miniatura" +msgstr "Creando Miniatura" #: editor/editor_node.cpp msgid "This operation can't be done without a tree root." @@ -1501,7 +1500,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Failed to load resource." -msgstr "Hubo un problema al cargar el recurso." +msgstr "Error al cargar el recurso." #: editor/editor_node.cpp msgid "Can't load MeshLibrary for merging!" @@ -1529,7 +1528,7 @@ msgstr "Se han sobrescrito los ajustes predeterminados del editor." #: editor/editor_node.cpp msgid "Layout name not found!" -msgstr "¡No se encuentra el nombre del ajuste!" +msgstr "¡Nombre del ajuste no encontrado!" #: editor/editor_node.cpp msgid "Restored default layout to base settings." @@ -1594,23 +1593,23 @@ msgstr "Ocultar todas las propiedades" #: editor/editor_node.cpp msgid "Copy Params" -msgstr "Copiar parámetros" +msgstr "Copiar Parámetros" #: editor/editor_node.cpp msgid "Paste Params" -msgstr "Pegar parámetros" +msgstr "Pegar Parámetros" #: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp msgid "Paste Resource" -msgstr "Pegar recurso" +msgstr "Pegar Recurso" #: editor/editor_node.cpp msgid "Copy Resource" -msgstr "Copiar recurso" +msgstr "Copiar Recurso" #: editor/editor_node.cpp msgid "Make Built-In" -msgstr "Convertirlo en integrado" +msgstr "Convertirlo en Integrado" #: editor/editor_node.cpp msgid "Make Sub-Resources Unique" @@ -1618,7 +1617,7 @@ msgstr "Creación de Subrecursos Únicos" #: editor/editor_node.cpp msgid "Open in Help" -msgstr "Abrir en la ayuda" +msgstr "Abrir en la Ayuda" #: editor/editor_node.cpp msgid "There is no defined scene to run." @@ -1711,11 +1710,11 @@ msgstr "Esta operación no puede realizarse sin una escena." #: editor/editor_node.cpp msgid "Export Mesh Library" -msgstr "Exportar biblioteca de mallas" +msgstr "Exportar Librería de Mallas" #: editor/editor_node.cpp msgid "This operation can't be done without a root node." -msgstr "Esta operación no puede realizarse sin un Nodo Raíz." +msgstr "Esta operación no puede realizarse sin un nodo raíz." #: editor/editor_node.cpp msgid "Export Tile Set" @@ -1727,7 +1726,7 @@ msgstr "Esta operación no puede realizarse sin un nodo seleccionado." #: editor/editor_node.cpp msgid "Current scene not saved. Open anyway?" -msgstr "La escena actual no se ha guardado. ¿Quieres abrirla de todos modos?" +msgstr "Escena actual no guardada ¿Abrir de todos modos?" #: editor/editor_node.cpp msgid "Can't reload a scene that was never saved." @@ -2320,7 +2319,7 @@ msgstr "Abrir Editor anterior" #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" -msgstr "Creando vistas previas de las mallas" +msgstr "Creación de Vistas Previas de Malla" #: editor/editor_plugin.cpp msgid "Thumbnail..." @@ -2851,7 +2850,7 @@ msgstr "Generando \"Lightmaps\"" #: editor/import/resource_importer_scene.cpp msgid "Generating for Mesh: " -msgstr "Generando para modelo: " +msgstr "Generando para Malla: " #: editor/import/resource_importer_scene.cpp msgid "Running Custom Script..." @@ -3122,7 +3121,7 @@ msgstr "Posterior" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Depth" -msgstr "Profundidad" +msgstr "Depth" #: editor/plugins/animation_player_editor_plugin.cpp msgid "1 step" @@ -3996,7 +3995,7 @@ msgstr "Clic derecho: Borrar punto." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh is empty!" -msgstr "¡La malla está vacía!" +msgstr "¡La Malla está vacía!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Static Trimesh Body" @@ -4020,11 +4019,11 @@ msgstr "Crear forma convexa" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Navigation Mesh" -msgstr "Crear malla de navegación" +msgstr "Crear Malla de Navegación" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Contained Mesh is not of type ArrayMesh." -msgstr "La malla que contiene no es del tipo ArrayMesh." +msgstr "La Malla contenedora no es del tipo ArrayMesh." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "UV Unwrap failed, mesh may not be manifold?" @@ -4042,7 +4041,7 @@ msgstr "El modelo no tiene UV en esta capa" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" -msgstr "¡MeshInstance no tiene malla!" +msgstr "¡MeshInstance no tiene Malla!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh has not surface to create outlines from!" @@ -4050,7 +4049,7 @@ msgstr "¡La malla no tiene superficie de la que crear contornos!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!" -msgstr "El tipo de la malla primitiva no es PRIMITIVE_TRIANGLES!" +msgstr "¡El tipo primitivo de malla no es PRIMITIVE_TRIANGLES!" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Could not create outline!" @@ -4098,7 +4097,7 @@ msgstr "Desenvuelva UV2 para Lightmap/AO" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh" -msgstr "Crear contorno de malla" +msgstr "Crear Contorno de Malla" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Outline Size:" @@ -4107,12 +4106,14 @@ msgstr "Tamaño del contorno:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "No mesh source specified (and no MultiMesh set in node)." msgstr "" -"No se especificó malla de origen (y no hay MultiMesh establecido en el nodo)." +"No se ha especificado ninguna malla de origen (y no hay MultiMesh " +"establecido en el nodo)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "No mesh source specified (and MultiMesh contains no Mesh)." msgstr "" -"No se especificó malla de origen (y MultiMesh no contiene ningún Mesh)." +"No se ha especificado ninguna malla de origen (y MultiMesh no contiene " +"ninguna Mesh)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (invalid path)." @@ -4120,11 +4121,11 @@ msgstr "El origen de la malla es inválido (ruta inválida)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (not a MeshInstance)." -msgstr "La malla elegida no es correcta (no es un MeshInstance)." +msgstr "El origen de la malla es inválido (no es un MeshInstance)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Mesh source is invalid (contains no Mesh resource)." -msgstr "La malla elegida no es correcta (no contiene ningún recurso Mesh)." +msgstr "El origen de la malla es inválido (no contiene ningún recurso Mesh)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "No surface source specified." @@ -4152,7 +4153,7 @@ msgstr "No se pudo mapear el área." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Source Mesh:" -msgstr "Elige un origen de malla:" +msgstr "Elige un origen de Malla:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Target Surface:" @@ -4172,7 +4173,7 @@ msgstr "Superficie objetivo:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Source Mesh:" -msgstr "Modelo 3D elegido:" +msgstr "Malla de origen:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "X-Axis" @@ -4212,11 +4213,11 @@ msgstr "¡Calcular!" #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Bake the navigation mesh." -msgstr "Pre-calcular la malla de navegación 3D." +msgstr "Pre-calcular la malla de navegación." #: editor/plugins/navigation_mesh_editor_plugin.cpp msgid "Clear the navigation mesh." -msgstr "Vaciar malla de navegación 3D." +msgstr "Vaciar malla de navegación." #: editor/plugins/navigation_mesh_generator.cpp msgid "Setting up Configuration..." @@ -4501,7 +4502,7 @@ msgstr "Crear mapa UV" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Transform UV Map" -msgstr "Transformar mapa UV" +msgstr "Transformar Mapa UV" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Polygon 2D UV Editor" @@ -5416,11 +5417,11 @@ msgstr "Activar Vista Libre" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform" -msgstr "Transform" +msgstr "Transformar" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Dialog..." -msgstr "Ventana de transformación..." +msgstr "Dialogo de Transformación..." #: editor/plugins/spatial_editor_plugin.cpp msgid "1 Viewport" @@ -5497,7 +5498,7 @@ msgstr "Profundidad máxima de vista:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Transform Change" -msgstr "Transformar" +msgstr "Cambio de Transformación" #: editor/plugins/spatial_editor_plugin.cpp msgid "Translate:" @@ -6644,7 +6645,7 @@ msgstr "Reemparentar ubicación (selecciona un nuevo padre):" #: editor/reparent_dialog.cpp msgid "Keep Global Transform" -msgstr "Mantener transformación global" +msgstr "Mantener Transformación Global" #: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp msgid "Reparent" @@ -7498,6 +7499,11 @@ msgstr "Compilar Proyecto" msgid "Warnings" msgstr "Advertencias" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Ver Archivos" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Fin del reporte de la pila de excepciones" diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po index 64ee2404f1..bea184b813 100644 --- a/editor/translations/es_AR.po +++ b/editor/translations/es_AR.po @@ -7464,6 +7464,11 @@ msgstr "Construir Proyecto" msgid "Warnings" msgstr "Advertencias" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Ver Archivos" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Fin del stack trace de excepción interna" diff --git a/editor/translations/fa.po b/editor/translations/fa.po index f674ef99cc..eb192ececb 100644 --- a/editor/translations/fa.po +++ b/editor/translations/fa.po @@ -2,26 +2,25 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # alabd14313 <alabd14313@yahoo.com>, 2016. # Dante Marshal <Marshal.Devilhunter@gmail.com>, 2018. # hamed nasib <cghamed752@chmail.ir>, 2016. # Hasan Hejdari Nasab <hsn6@openmailbox.org>, 2017. # rezapouya <r.pouya@chmail.ir>, 2016. # sayyed hamed nasib <cghamed752@chmail.ir>, 2017. -# +# Behrooz Kashani <bkashani@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-04-29 05:39+0000\n" -"Last-Translator: Dante Marshal <Marshal.Devilhunter@gmail.com>\n" +"PO-Revision-Date: 2018-07-24 19:42+0000\n" +"Last-Translator: Behrooz Kashani <bkashani@gmail.com>\n" "Language-Team: Persian <https://hosted.weblate.org/projects/godot-engine/" "godot/fa/>\n" "Language: fa\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -636,9 +635,8 @@ msgstr "" "آیا در هر صورت حذف شوند؟(بدون برگشت)" #: editor/dependency_editor.cpp -#, fuzzy msgid "Cannot remove:" -msgstr "ناتوانی در حذف :" +msgstr "امکان حذف وجود ندارد :" #: editor/dependency_editor.cpp msgid "Error loading:" @@ -743,9 +741,8 @@ msgid "Gold Sponsors" msgstr "اسپانسرهای درجه ۲" #: editor/editor_about.cpp -#, fuzzy msgid "Mini Sponsors" -msgstr "اسپانسرهای دیگر" +msgstr "اسپانسرهای کوچک" #: editor/editor_about.cpp msgid "Gold Donors" @@ -789,7 +786,7 @@ msgstr "اجزا" #: editor/editor_about.cpp msgid "Licenses" -msgstr "" +msgstr "گواهینامه" #: editor/editor_asset_installer.cpp editor/project_manager.cpp msgid "Error opening package file, not in zip format." @@ -7420,6 +7417,11 @@ msgstr "پروژه" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "نمایش پرونده ها" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/fi.po b/editor/translations/fi.po index f80efffd42..11f9bb51c7 100644 --- a/editor/translations/fi.po +++ b/editor/translations/fi.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-14 20:37+0000\n" +"PO-Revision-Date: 2018-07-19 10:36+0000\n" "Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" @@ -20,7 +20,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0.1\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -2128,7 +2128,7 @@ msgstr "Käynnistä muokattavana oleva skene." #: editor/editor_node.cpp msgid "Play Scene" -msgstr "Toista skene" +msgstr "Pelaa skeneä" #: editor/editor_node.cpp msgid "Play custom scene" @@ -2414,7 +2414,7 @@ msgstr "Lataa uudelleen" #: editor/export_template_manager.cpp msgid "Uninstall" -msgstr "Poista" +msgstr "Poista asennus" #: editor/export_template_manager.cpp msgid "(Installed)" @@ -2475,7 +2475,7 @@ msgstr "" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Can't resolve." -msgstr "Yhdistäminen epäonnistui." +msgstr "Yhdeydenselvitys epäonnistui." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2520,11 +2520,11 @@ msgstr "Yhteys katkaistu" #: editor/export_template_manager.cpp msgid "Resolving" -msgstr "Selvitetään" +msgstr "Selvitetään yhteyttä" #: editor/export_template_manager.cpp msgid "Can't Resolve" -msgstr "Yhdistäminen epäonnistui" +msgstr "Yhteyden selvittäminen epäonnistui" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -2809,7 +2809,7 @@ msgstr "Luodaan meshille: " #: editor/import/resource_importer_scene.cpp msgid "Running Custom Script..." -msgstr "Suorita valitsemasi skripti..." +msgstr "Suoritetaan mukautettua skriptiä..." #: editor/import/resource_importer_scene.cpp msgid "Couldn't load post-import script:" @@ -2940,7 +2940,7 @@ msgstr "VIRHE: Virheellinen animaation nimi!" #: editor/plugins/animation_player_editor_plugin.cpp msgid "ERROR: Animation name already exists!" -msgstr "VIrhe: Samanniminen animaatio on jo olemassa!" +msgstr "VIRHE: Samanniminen animaatio on jo olemassa!" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp @@ -3316,7 +3316,8 @@ msgstr "Pyyntö epäonnistui, liikaa uudelleenohjauksia" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Bad download hash, assuming file has been tampered with." -msgstr "Latauksessa väärä hash, oletetaan että tiedostoa on näpelöity." +msgstr "" +"Latauksessa väärä hajautuskoodi, oletetaan että tiedostoa on näpelöity." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Expected:" @@ -3328,7 +3329,7 @@ msgstr "Saatiin:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Failed sha256 hash check" -msgstr "sha256 hash-tarkistus epäonnistui" +msgstr "sha256-hajautusarvon tarkistus epäonnistui" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Asset Download Error:" @@ -3442,7 +3443,7 @@ msgstr "" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "Failed creating lightmap images, make sure path is writable." msgstr "" -"Lightmap-kuvien luonti epäonnistui, varmista, että polku on " +"Lightmap-kuvien luonti epäonnistui. Varmista, että polku on " "kirjoituskelpoinen." #: editor/plugins/baked_lightmap_editor_plugin.cpp @@ -5301,11 +5302,11 @@ msgstr "Tarttumisen tila (%s)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" -msgstr "Pohjanäkymä" +msgstr "Alanäkymä" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View" -msgstr "Huippunäkymä" +msgstr "Ylänäkymä" #: editor/plugins/spatial_editor_plugin.cpp msgid "Rear View" @@ -7110,7 +7111,7 @@ msgstr "Aseta puusta" #: editor/settings_config_dialog.cpp msgid "Shortcuts" -msgstr "Pikakuvakkeet" +msgstr "Pikanäppäimet" #: editor/settings_config_dialog.cpp msgid "Binding" @@ -7435,6 +7436,11 @@ msgstr "Käännä projekti" msgid "Warnings" msgstr "Varoitukset" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Näytä tiedostot" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Sisemmän poikkeuksen kutsupinon loppu" diff --git a/editor/translations/fr.po b/editor/translations/fr.po index ee1d7b2cad..5c28d84a90 100644 --- a/editor/translations/fr.po +++ b/editor/translations/fr.po @@ -40,12 +40,14 @@ # Tommy Melançon-Roy <tommel1234@hotmail.com>, 2017-2018. # Willow <theotimefd@aol.com>, 2018. # Xananax <xananax@yelostudio.com>, 2017-2018. +# Perrier Mathis <mathis.perrier73@gmail.com>, 2018. +# Ewan Lehnebach <ewan.lehnebach@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-06-12 16:38+0000\n" -"Last-Translator: Philippe Gervaise <blah@malvese.org>\n" +"PO-Revision-Date: 2018-07-21 12:37+0000\n" +"Last-Translator: Perrier Mathis <mathis.perrier73@gmail.com>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/" "godot/fr/>\n" "Language: fr\n" @@ -53,7 +55,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 3.0.1\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -65,11 +67,11 @@ msgstr "Toute la sélection" #: editor/animation_editor.cpp msgid "Anim Change Keyframe Time" -msgstr "Changer l'heure de l'animation des images clés" +msgstr "Animation Changer l'heure de l'image clé" #: editor/animation_editor.cpp msgid "Anim Change Transition" -msgstr "Transition du changement d'animation" +msgstr "Animation Changer la transition" #: editor/animation_editor.cpp msgid "Anim Change Transform" @@ -77,7 +79,7 @@ msgstr "Animation Changer la transformation" #: editor/animation_editor.cpp msgid "Anim Change Keyframe Value" -msgstr "Changer la valeur de l'animation des images clés" +msgstr "Animation Changer la valeur de l'image clé" #: editor/animation_editor.cpp msgid "Anim Change Call" @@ -4076,7 +4078,7 @@ msgstr "Créer le contour" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "Maillage" +msgstr "Maillages" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -6512,7 +6514,7 @@ msgstr "Remaps par langue :" #: editor/project_settings_editor.cpp msgid "Locale" -msgstr "Langue" +msgstr "Localisation" #: editor/project_settings_editor.cpp msgid "Locales Filter" @@ -7513,6 +7515,11 @@ msgstr "Compiler le projet" msgid "Warnings" msgstr "Avertissements" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Voir Fichiers" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Fin de la trace d'appel (stack trace) intrinsèque" diff --git a/editor/translations/he.po b/editor/translations/he.po index 0f1881211f..6dfd0ab3a5 100644 --- a/editor/translations/he.po +++ b/editor/translations/he.po @@ -7294,6 +7294,10 @@ msgstr "" msgid "Warnings" msgstr "אזהרות" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/hi.po b/editor/translations/hi.po index 3340f13471..606da1d118 100644 --- a/editor/translations/hi.po +++ b/editor/translations/hi.po @@ -7327,6 +7327,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/hu.po b/editor/translations/hu.po index b04dd073df..84c64138dc 100644 --- a/editor/translations/hu.po +++ b/editor/translations/hu.po @@ -7405,6 +7405,11 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Fájlok Megtekintése" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/id.po b/editor/translations/id.po index 3956378ce7..a0356b8178 100644 --- a/editor/translations/id.po +++ b/editor/translations/id.po @@ -10,7 +10,7 @@ # Fajar Ru <kzofajar@gmail.com>, 2018. # Khairul Hidayat <khairulcyber4rt@gmail.com>, 2016. # Reza Hidayat Bayu Prabowo <rh.bayu.prabowo@gmail.com>, 2018. -# Romi Kusuma Bakti <romikusumab@gmail.com>, 2017. +# Romi Kusuma Bakti <romikusumab@gmail.com>, 2017, 2018. # Sofyan Sugianto <sofyanartem@gmail.com>, 2017-2018. # Tito <ijavadroid@gmail.com>, 2018. # Tom My <tom.asadinawan@gmail.com>, 2017. @@ -18,8 +18,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-22 08:30+0000\n" -"Last-Translator: Fajar Ru <kzofajar@gmail.com>\n" +"PO-Revision-Date: 2018-07-15 12:38+0000\n" +"Last-Translator: Romi Kusuma Bakti <romikusumab@gmail.com>\n" "Language-Team: Indonesian <https://hosted.weblate.org/projects/godot-engine/" "godot/id/>\n" "Language: id\n" @@ -917,9 +917,8 @@ msgid "Delete Audio Bus" msgstr "Hapus Bus Audio" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Duplicate Audio Bus" -msgstr "Duplikat Audio Bus" +msgstr "Duplikatkan Bus Audio" #: editor/editor_audio_buses.cpp msgid "Reset Bus Volume" @@ -976,9 +975,8 @@ msgid "Save this Bus Layout to a file." msgstr "Simpan Layout Bus ke berkas." #: editor/editor_audio_buses.cpp editor/import_dock.cpp -#, fuzzy msgid "Load Default" -msgstr "Muat Konfigurasi Bawaan" +msgstr "Muat Default" #: editor/editor_audio_buses.cpp msgid "Load the default Bus Layout." @@ -1294,18 +1292,16 @@ msgid "Members:" msgstr "Member-member:" #: editor/editor_help.cpp -#, fuzzy msgid "Public Methods" -msgstr "Fungsi Publik" +msgstr "Metode Publik" #: editor/editor_help.cpp msgid "Public Methods:" msgstr "Metode Publik:" #: editor/editor_help.cpp -#, fuzzy msgid "GUI Theme Items" -msgstr "Item-item Tema GUI:" +msgstr "Item Tema GUI" #: editor/editor_help.cpp msgid "GUI Theme Items:" @@ -1422,9 +1418,8 @@ msgstr "Simpan Resource Sebagai..." #: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp #: editor/scene_tree_dock.cpp -#, fuzzy msgid "I see..." -msgstr "Aku tahu..." +msgstr "Mengerti..." #: editor/editor_node.cpp msgid "Can't open file for writing:" @@ -1471,9 +1466,8 @@ msgid "Creating Thumbnail" msgstr "Membuat Thumbnail" #: editor/editor_node.cpp -#, fuzzy msgid "This operation can't be done without a tree root." -msgstr "Tindakan ini tidak bisa dilakukan tanpa \"tree root\"" +msgstr "Operasi ini tidak dapat diselesaikan tanpa root pohon." #: editor/editor_node.cpp msgid "" @@ -2712,7 +2706,7 @@ msgstr "Buka Scene" #: editor/filesystem_dock.cpp msgid "Instance" -msgstr "" +msgstr "Instance" #: editor/filesystem_dock.cpp msgid "Edit Dependencies..." @@ -2821,18 +2815,16 @@ msgid "Importing Scene..." msgstr "Mengimpor scene..." #: editor/import/resource_importer_scene.cpp -#, fuzzy msgid "Generating Lightmaps" -msgstr "Sedang Membuat Pemetaan Cahaya" +msgstr "Membuat Pemetaan Cahaya" #: editor/import/resource_importer_scene.cpp msgid "Generating for Mesh: " msgstr "" #: editor/import/resource_importer_scene.cpp -#, fuzzy msgid "Running Custom Script..." -msgstr "Menjalankan Skrip Buatan..." +msgstr "Menjalankan Script Khusus..." #: editor/import/resource_importer_scene.cpp msgid "Couldn't load post-import script:" @@ -2868,7 +2860,7 @@ msgstr "Impor sebagai:" #: editor/import_dock.cpp editor/property_editor.cpp msgid "Preset..." -msgstr "" +msgstr "Prasetel..." #: editor/import_dock.cpp msgid "Reimport" @@ -7563,6 +7555,11 @@ msgstr "Proyek" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "File:" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/is.po b/editor/translations/is.po index 98a376edca..0d6200fba1 100644 --- a/editor/translations/is.po +++ b/editor/translations/is.po @@ -7301,6 +7301,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/it.po b/editor/translations/it.po index 2d566fe163..afb8c5cfb8 100644 --- a/editor/translations/it.po +++ b/editor/translations/it.po @@ -2,7 +2,6 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # Alessio Corridori <alessiocorridori@hotmail.com>, 2018. # Dario Bonfanti <bonfi.96@hotmail.it>, 2016-2017. # Dario D'Ambra <legione0@gmail.com>, 2017. @@ -15,13 +14,13 @@ # RealAquilus <JamesHeller@live.it>, 2017. # Samuele Zolfanelli <samdazel@gmail.com>, 2018. # Sean Bone <seanbone@zumguy.com>, 2017. -# +# Red Pill <redpill902@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-05-18 16:39+0000\n" -"Last-Translator: Alessio Corridori <alessiocorridori@hotmail.com>\n" +"PO-Revision-Date: 2018-06-25 18:40+0000\n" +"Last-Translator: Red Pill <redpill902@gmail.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot/it/>\n" "Language: it\n" @@ -29,7 +28,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -40,9 +39,8 @@ msgid "All Selection" msgstr "Seleziona Tutto" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Time" -msgstr "Anim Cambia Valore" +msgstr "Anim Cambia Tempo di Keyframe" #: editor/animation_editor.cpp msgid "Anim Change Transition" @@ -7640,6 +7638,11 @@ msgstr "Progetto" msgid "Warnings" msgstr "Avvertimento" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Vedi Files" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ja.po b/editor/translations/ja.po index 5ce73d0442..d7e2c07ac2 100644 --- a/editor/translations/ja.po +++ b/editor/translations/ja.po @@ -8251,6 +8251,11 @@ msgstr "プロジェクト" msgid "Warnings" msgstr "警告" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "ビューファイル:" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ko.po b/editor/translations/ko.po index be6b540a9a..197a11efa2 100644 --- a/editor/translations/ko.po +++ b/editor/translations/ko.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-06-07 16:40+0000\n" -"Last-Translator: pgyage3263 <pgyage3263@naver.com>\n" +"PO-Revision-Date: 2018-07-21 04:38+0000\n" +"Last-Translator: 송태섭 <xotjq237@gmail.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/" "godot/ko/>\n" "Language: ko\n" @@ -23,7 +23,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 3.0\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -4003,7 +4003,7 @@ msgstr "외곽선 만들기" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "메시" +msgstr "Mesh" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -7412,6 +7412,11 @@ msgstr "프로젝트 빌드" msgid "Warnings" msgstr "경고" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "파일 보기" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "내부 예외 스택 추적의 끝" diff --git a/editor/translations/lt.po b/editor/translations/lt.po index bf4443627a..639e086d4c 100644 --- a/editor/translations/lt.po +++ b/editor/translations/lt.po @@ -7295,6 +7295,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ms.po b/editor/translations/ms.po index 19d8b6b7d8..09c7b39e08 100644 --- a/editor/translations/ms.po +++ b/editor/translations/ms.po @@ -2,22 +2,21 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # Sam Vanguard <syafz119@gmail.com>, 2018. # Shaqir Rafiq <moshamoradev@gmail.com>, 2018. -# +# Syaz Amirin <amirin123z@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-05 19:27+0000\n" -"Last-Translator: Shaqir Rafiq <moshamoradev@gmail.com>\n" +"PO-Revision-Date: 2018-06-30 09:40+0000\n" +"Last-Translator: Syaz Amirin <amirin123z@gmail.com>\n" "Language-Team: Malay <https://hosted.weblate.org/projects/godot-engine/godot/" "ms/>\n" "Language: ms\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 3.0\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -2014,11 +2013,11 @@ msgstr "" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp msgid "Community" -msgstr "" +msgstr "Komuniti" #: editor/editor_node.cpp msgid "About" -msgstr "" +msgstr "Tentang" #: editor/editor_node.cpp msgid "Play the project." @@ -6244,7 +6243,7 @@ msgstr "" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "General" -msgstr "" +msgstr "Am" #: editor/project_settings_editor.cpp editor/property_editor.cpp msgid "Property:" @@ -7273,6 +7272,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/nb.po b/editor/translations/nb.po index e76053150c..abc026771d 100644 --- a/editor/translations/nb.po +++ b/editor/translations/nb.po @@ -14,8 +14,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-22 08:31+0000\n" -"Last-Translator: Frank T. Rambol <frank@d-fect.com>\n" +"PO-Revision-Date: 2018-06-28 14:40+0000\n" +"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n" "Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/godot-" "engine/godot/nb/>\n" "Language: nb\n" @@ -1756,7 +1756,9 @@ msgstr "Velg en HovedScene" #: editor/editor_node.cpp #, fuzzy msgid "Unable to enable addon plugin at: '%s' parsing of config failed." -msgstr "Kan ikke aktivere addon-plugin på: '%s' parsing av konfig feilet." +msgstr "" +"Kan ikke aktivere tilleggs-programtillegg på: \"%s\" tolking av oppsett " +"mislyktes." #: editor/editor_node.cpp msgid "Unable to find script field for addon plugin at: 'res://addons/%s'." @@ -1912,9 +1914,8 @@ msgid "MeshLibrary..." msgstr "MeshBibliotek..." #: editor/editor_node.cpp -#, fuzzy msgid "TileSet..." -msgstr "TileSet…" +msgstr "Flissett…" #: editor/editor_node.cpp editor/plugins/script_text_editor.cpp #: scene/gui/line_edit.cpp scene/gui/text_edit.cpp @@ -7495,6 +7496,11 @@ msgstr "Prosjekt" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Vis Filer" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/nl.po b/editor/translations/nl.po index bfedf322b3..240d99182f 100644 --- a/editor/translations/nl.po +++ b/editor/translations/nl.po @@ -2,7 +2,6 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # aelspire <aelspire@gmail.com>, 2017. # Aram Nap <xyphex.aram@gmail.com>, 2017. # Arjan219 <arjannugteren1@gmail.com>, 2017-2018. @@ -24,19 +23,19 @@ # Willem <studiebolmail@gmail.com>, 2018. # Wout Standaert <wout@blobkat.com>, 2017. # Zatherz <zatherz@linux.pl>, 2017. -# +# Tahar Meijs <tntmeijs@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-05-21 18:36+0000\n" -"Last-Translator: Johannes Smit <smitjohannes96@gmail.com>\n" +"PO-Revision-Date: 2018-06-30 15:36+0000\n" +"Last-Translator: Tahar Meijs <tntmeijs@gmail.com>\n" "Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/" "nl/>\n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -6427,7 +6426,7 @@ msgstr "" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "General" -msgstr "" +msgstr "Algemeen" #: editor/project_settings_editor.cpp editor/property_editor.cpp msgid "Property:" @@ -7492,6 +7491,11 @@ msgstr "Project" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Bekijk Bestanden" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/pl.po b/editor/translations/pl.po index 5ca2760249..a133a4d8d5 100644 --- a/editor/translations/pl.po +++ b/editor/translations/pl.po @@ -27,7 +27,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-22 08:31+0000\n" +"PO-Revision-Date: 2018-07-14 08:42+0000\n" "Last-Translator: RM <synaptykq@gmail.com>\n" "Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/" "godot/pl/>\n" @@ -1975,7 +1975,7 @@ msgstr "Wyjdź do Listy Projektów" #: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp msgid "Debug" -msgstr "Debuguj" +msgstr "Debugowanie" #: editor/editor_node.cpp msgid "Deploy with Remote Debug" @@ -3093,7 +3093,7 @@ msgstr "Poprzednie" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Future" -msgstr "Następne" +msgstr "Przyszłość" #: editor/plugins/animation_player_editor_plugin.cpp msgid "Depth" @@ -3154,7 +3154,7 @@ msgstr "Czas Przejścia Między Animacjami" #: editor/plugins/animation_player_editor_plugin.cpp #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Animation" -msgstr "Animacja" +msgstr "Animacje" #: editor/plugins/animation_tree_editor_plugin.cpp msgid "New name:" @@ -6514,7 +6514,7 @@ msgstr "Mapowanie w zależności od lokalizacji:" #: editor/project_settings_editor.cpp msgid "Locale" -msgstr "Lokalizacja" +msgstr "Języki" #: editor/project_settings_editor.cpp msgid "Locales Filter" @@ -7137,7 +7137,7 @@ msgstr "Monitor" #: editor/script_editor_debugger.cpp msgid "Value" -msgstr "Wartość" +msgstr "Value" #: editor/script_editor_debugger.cpp msgid "Monitors" @@ -7525,6 +7525,11 @@ msgstr "Zbuduj projekt" msgid "Warnings" msgstr "Ostrzeżenia" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Pokaż pliki" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" @@ -7672,11 +7677,11 @@ msgstr "Dodaj węzeł(y) z drzewa" #: modules/visual_script/visual_script_editor.cpp msgid "Add Getter Property" -msgstr "" +msgstr "Dodaj właściwość Gettera" #: modules/visual_script/visual_script_editor.cpp msgid "Add Setter Property" -msgstr "" +msgstr "Dodaj właściwość Settera" #: modules/visual_script/visual_script_editor.cpp msgid "Change Base Type" @@ -7712,7 +7717,7 @@ msgstr "Iterator" #: modules/visual_script/visual_script_editor.cpp msgid "While" -msgstr "" +msgstr "While" #: modules/visual_script/visual_script_editor.cpp msgid "Return" @@ -8045,7 +8050,7 @@ msgstr "" #: scene/3d/arvr_nodes.cpp msgid "ARVRCamera must have an ARVROrigin node as its parent" -msgstr "ARVRCamera musi dziedziczyć po ARVROrigin node" +msgstr "ARVRCamera musi dziedziczyć po węźle ARVROrigin" #: scene/3d/arvr_nodes.cpp #, fuzzy @@ -8073,11 +8078,11 @@ msgstr "" #: scene/3d/arvr_nodes.cpp msgid "ARVROrigin requires an ARVRCamera child node" -msgstr "ARVROrigin wymaga by ARVRCamera dziedziczyła po node" +msgstr "ARVROrigin wymaga dziedziczącego po nim ARVRCamera" #: scene/3d/baked_lightmap.cpp msgid "%d%%" -msgstr "" +msgstr "%d%%" #: scene/3d/baked_lightmap.cpp msgid "(Time Left: %d:%02d s)" @@ -8106,6 +8111,10 @@ msgid "" "Consider adding CollisionShape or CollisionPolygon children nodes to define " "its shape." msgstr "" +"Ten węzeł nie posiada podwezła, który definiowałby jego kształt, więc nie " +"może wchodzić w interakcje z przestrzenią.\n" +"Powinieneś dodać węzeł \"CollisionShape2D\" lub \"CollisionPolygon2D\" jako " +"jego podwęzeł aby zdefiniować jego kształt." #: scene/3d/collision_polygon.cpp msgid "" diff --git a/editor/translations/pr.po b/editor/translations/pr.po index 0c085024e0..ee30748cee 100644 --- a/editor/translations/pr.po +++ b/editor/translations/pr.po @@ -7350,6 +7350,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po index 6d26cbc500..472fb1e7bc 100644 --- a/editor/translations/pt_BR.po +++ b/editor/translations/pt_BR.po @@ -23,12 +23,13 @@ # Renato Rotenberg <renato.rotenberg@gmail.com>, 2017. # Rodolfo R Gomes <rodolforg@gmail.com>, 2017-2018. # Tiago Almeida <thyagoeap@gmail.com>, 2017. +# Mauricio Luan Carneiro deSouza <newmailmlcs@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: 2016-05-30\n" -"PO-Revision-Date: 2018-06-16 18:43+0000\n" -"Last-Translator: Rodolfo R Gomes <rodolforg@gmail.com>\n" +"PO-Revision-Date: 2018-07-26 09:14+0000\n" +"Last-Translator: Mauricio Luan Carneiro deSouza <newmailmlcs@gmail.com>\n" "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_BR/>\n" "Language: pt_BR\n" @@ -36,7 +37,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 3.0.1\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -4036,7 +4037,7 @@ msgstr "Criar Contorno" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "Mesh" +msgstr "Malha" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -5100,7 +5101,7 @@ msgstr "Transformação do Eixo-Z." #: editor/plugins/spatial_editor_plugin.cpp msgid "View Plane Transform." -msgstr "Visualizar Transformação do Plano." +msgstr "Transformação do Plano de Visão." #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -5236,7 +5237,7 @@ msgstr "Visualizar Gizmos" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Information" -msgstr "VIsualizar Informação" +msgstr "Visualizar Informações" #: editor/plugins/spatial_editor_plugin.cpp msgid "View FPS" @@ -7049,7 +7050,7 @@ msgstr "Processo Filho Conectado" #: editor/script_editor_debugger.cpp msgid "Copy Error" -msgstr "Erro ao Copiar" +msgstr "Copiar Erro" #: editor/script_editor_debugger.cpp msgid "Inspect Previous Instance" @@ -7463,6 +7464,11 @@ msgstr "Compilar Projeto" msgid "Warnings" msgstr "Avisos" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Ver Arquivos" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Fim da pilha de rastreamento de exceção interna" diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po index 71275cd19a..d111d1dd4b 100644 --- a/editor/translations/pt_PT.po +++ b/editor/translations/pt_PT.po @@ -7442,6 +7442,11 @@ msgstr "Construir Projeto" msgid "Warnings" msgstr "Avisos" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Ver Ficheiros" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Fim do stack trace de exceção interna" diff --git a/editor/translations/ro.po b/editor/translations/ro.po index eaf931092a..c5a4e35903 100644 --- a/editor/translations/ro.po +++ b/editor/translations/ro.po @@ -7400,6 +7400,11 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Vizualizează Fișierele" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ru.po b/editor/translations/ru.po index 97c7284404..1c888d3330 100644 --- a/editor/translations/ru.po +++ b/editor/translations/ru.po @@ -17,12 +17,15 @@ # Sergey <maligin.serega2010@yandex.ru>, 2018. # Sergey Agarkov <zorgsoft@gmail.com>, 2017. # teriva <spirin.cos@yandex.ru>, 2018. +# Aleksey Terentyev <terentjew.alexey@ya.ru>, 2018. +# Игорь Д <protorian.di@gmail.com>, 2018. +# Егор Бураков <fend.q@mail.ru>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2018-06-18 19:42+0000\n" -"Last-Translator: ijet <my-ijet@mail.ru>\n" +"PO-Revision-Date: 2018-07-22 04:43+0000\n" +"Last-Translator: Егор Бураков <fend.q@mail.ru>\n" "Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/" "godot/ru/>\n" "Language: ru\n" @@ -31,7 +34,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 3.0.1\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -2347,7 +2350,7 @@ msgstr "Кадр %" #: editor/editor_profiler.cpp msgid "Physics Frame %" -msgstr "Физический шаг %" +msgstr "Кадр физики %" #: editor/editor_profiler.cpp editor/script_editor_debugger.cpp msgid "Time:" @@ -2359,7 +2362,7 @@ msgstr "Включительно" #: editor/editor_profiler.cpp msgid "Self" -msgstr "" +msgstr "Субъект" #: editor/editor_profiler.cpp msgid "Frame #:" @@ -4029,7 +4032,7 @@ msgstr "Создать контур" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Mesh" -msgstr "Полисетка" +msgstr "Массив" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Trimesh Static Body" @@ -5561,7 +5564,7 @@ msgstr "Предпросмотр StyleBox:" #: editor/plugins/style_box_editor_plugin.cpp msgid "StyleBox" -msgstr "" +msgstr "StyleBox" #: editor/plugins/texture_region_editor_plugin.cpp msgid "Set Region Rect" @@ -5684,14 +5687,12 @@ msgid "Checked Item" msgstr "Отмеченный элемент" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Radio Item" -msgstr "Добавить элемент" +msgstr "Переключатель" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Checked Radio Item" -msgstr "Отмеченный элемент" +msgstr "Отмеченный переключатель" #: editor/plugins/theme_editor_plugin.cpp msgid "Has" @@ -7233,7 +7234,7 @@ msgstr "Библиотеки: " #: modules/gdnative/register_types.cpp msgid "GDNative" -msgstr "" +msgstr "GDNative" #: modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -7450,6 +7451,11 @@ msgstr "Собрать проект" msgid "Warnings" msgstr "Предупреждения" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Просмотр Файлов" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Конец трассировки внутреннего стека исключений" diff --git a/editor/translations/sk.po b/editor/translations/sk.po index 9716dee696..94b6c137d0 100644 --- a/editor/translations/sk.po +++ b/editor/translations/sk.po @@ -7354,6 +7354,11 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Súbor:" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/sl.po b/editor/translations/sl.po index 0fe619654f..66e3c43bee 100644 --- a/editor/translations/sl.po +++ b/editor/translations/sl.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-10 08:44+0000\n" +"PO-Revision-Date: 2018-06-27 13:43+0000\n" "Last-Translator: matevž lapajne <sivar.lapajne@gmail.com>\n" "Language-Team: Slovenian <https://hosted.weblate.org/projects/godot-engine/" "godot/sl/>\n" @@ -19,7 +19,7 @@ msgstr "" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" "%100==4 ? 2 : 3;\n" -"X-Generator: Weblate 3.0.1-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -3340,23 +3340,23 @@ msgstr "Razreševanje..." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Error making request" -msgstr "" +msgstr "Napaka pri izdelavi zahteve" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Idle" -msgstr "" +msgstr "Nedejaven" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Retry" -msgstr "" +msgstr "Ponovi" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download Error" -msgstr "" +msgstr "Napaka Pri Prenosu" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download for this asset is already in progress!" -msgstr "" +msgstr "Prenos za ta dodatek je že v teku!" #: editor/plugins/asset_library_editor_plugin.cpp msgid "first" @@ -3382,7 +3382,7 @@ msgstr "Vse" #: editor/plugins/asset_library_editor_plugin.cpp #: editor/project_settings_editor.cpp msgid "Plugins" -msgstr "" +msgstr "Vtičniki" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Sort:" @@ -3390,7 +3390,7 @@ msgstr "Razvrsti:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Reverse" -msgstr "" +msgstr "Obrni" #: editor/plugins/asset_library_editor_plugin.cpp #: editor/project_settings_editor.cpp @@ -3407,15 +3407,15 @@ msgstr "Podpora..." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Official" -msgstr "" +msgstr "Uradno" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Testing" -msgstr "" +msgstr "Preskušanje" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Assets ZIP File" -msgstr "" +msgstr "Dodatki v ZIP Datoteki" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -3423,6 +3423,9 @@ msgid "" "Save your scene (for images to be saved in the same dir), or pick a save " "path from the BakedLightmap properties." msgstr "" +"Ni mogoče določiti poti shranjevanja slik svetlobnih kart.\n" +"Shrani prizor (za slike, da bodo shranjene v isti mapi), ali izberi pot za " +"shranitev iz lastnosti Zapečene Svetlobne karte." #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -3435,53 +3438,54 @@ msgstr "" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "Failed creating lightmap images, make sure path is writable." msgstr "" +"Napaka pri izdelavi slik, svetlobnih kart. Poskrbite, da je pot zapisljiva." #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "Bake Lightmaps" -msgstr "" +msgstr "Zapeči Svetlobne karte" #: editor/plugins/camera_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Preview" -msgstr "" +msgstr "Predogled" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap" -msgstr "" +msgstr "Nastavi Zaskok" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Offset:" -msgstr "" +msgstr "Mrežni Zamik:" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Step:" -msgstr "" +msgstr "Mrežni Korak:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Rotation Offset:" -msgstr "" +msgstr "Rotacijski Odmik:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Rotation Step:" -msgstr "" +msgstr "Rotacijski Korak:" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move Pivot" -msgstr "" +msgstr "Premakni Točko" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move Action" -msgstr "" +msgstr "Premakni Dejanje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move vertical guide" -msgstr "" +msgstr "Premakni navpični vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Create new vertical guide" -msgstr "" +msgstr "Ustvari nov navpični vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Remove vertical guide" @@ -3489,59 +3493,61 @@ msgstr "Odstranite navpični vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Move horizontal guide" -msgstr "" +msgstr "Premakni vodoravni vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Create new horizontal guide" -msgstr "" +msgstr "Ustvari nov vodoravni vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Remove horizontal guide" -msgstr "Odstrani vodoravno vodilo" +msgstr "Odstrani vodoravni vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Create new horizontal and vertical guides" -msgstr "" +msgstr "Ustvari nov vodoravni in navpični vodnik" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Edit IK Chain" -msgstr "" +msgstr "Uredi Verigo IK" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Edit CanvasItem" -msgstr "" +msgstr "Uredi Platno Stvari" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Anchors only" -msgstr "" +msgstr "Samo Sidrišča" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Change Anchors and Margins" -msgstr "" +msgstr "Spremeni Sidrišča in Robove" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Change Anchors" -msgstr "" +msgstr "Spremeni Sidrišča" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Paste Pose" -msgstr "" +msgstr "Prilepi Pozicijo" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Select Mode" -msgstr "Izberite Način" +msgstr "Izberi Način" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Drag: Rotate" -msgstr "Povlecite: Zavrti" +msgstr "Povleci: Vrtenje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Alt+Drag: Move" -msgstr "" +msgstr "Alt+Drag: Premakni" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)." msgstr "" +"Pritisni 'v' za Spremembo Točke in 'Shift+v' za Vleko Točke (med " +"premikanjem)." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Alt+RMB: Depth list selection" @@ -3561,16 +3567,16 @@ msgid "" "Show a list of all objects at the position clicked\n" "(same as Alt+RMB in select mode)." msgstr "" -"Ob kliku prikaži seznam vseh objektov na tem mestu.\n" +"Ob kliku prikaži seznam vseh objektov na tem mestu\n" "(isto kot Alt+RMB v načinu izbire)." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Click to change object's rotation pivot." -msgstr "" +msgstr "Klikni, če želiš spremeniti rotacijsko točko objekta." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Pan Mode" -msgstr "" +msgstr "Način Plošče" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Toggles snapping" @@ -3578,66 +3584,66 @@ msgstr "Preklopi pripenjanje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Snap" -msgstr "" +msgstr "Uporabi Pripenjanje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snapping options" -msgstr "" +msgstr "Možnosti pripenjanja" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to grid" -msgstr "" +msgstr "Pripni na mrežo" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Rotation Snap" -msgstr "" +msgstr "Uporabi Rotacijsko Pripenjanje" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Configure Snap..." -msgstr "Preoblikuj Zaskok..." +msgstr "Nastavi Pripenjanje..." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap Relative" -msgstr "" +msgstr "Pripni Relativno" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Pixel Snap" -msgstr "" +msgstr "Uporabi Pripenjanje Pikslov" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Smart snapping" -msgstr "" +msgstr "Pametno pripenjanje" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to parent" -msgstr "" +msgstr "Pripni na Predhodnika" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to node anchor" -msgstr "" +msgstr "Pripni na gradnik vodilo" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to node sides" -msgstr "" +msgstr "Pripni na gradnik strani" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to other nodes" -msgstr "" +msgstr "Pripni na druge gradnike" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to guides" -msgstr "" +msgstr "Pripni na vodnike" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Lock the selected object in place (can't be moved)." -msgstr "Izbrani predmet zaklenite na svoje mesto (ga ni mogoče premakniti)." +msgstr "Izbrani predmet zakleni na svoje mesto (ni ga mogoče premakniti)." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Unlock the selected object (can be moved)." -msgstr "Odklenite izbrani predmet (ga lahko premaknete)." +msgstr "Odkleni izbrani predmet (lahko ga premaknete)." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Makes sure the object's children are not selectable." @@ -5945,9 +5951,8 @@ msgid "Imported Project" msgstr "" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid Project Name." -msgstr "Ime Projekta:" +msgstr "Neveljavno Ime Projekta." #: editor/project_manager.cpp msgid "Couldn't create folder." @@ -7374,6 +7379,11 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Ogled datotek" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po index c838174131..9998a16e3a 100644 --- a/editor/translations/sr_Cyrl.po +++ b/editor/translations/sr_Cyrl.po @@ -7461,6 +7461,11 @@ msgstr "Пројекат" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Погледај датотеке" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/sr_Latn.po b/editor/translations/sr_Latn.po index 975418d4fb..4d293a592d 100644 --- a/editor/translations/sr_Latn.po +++ b/editor/translations/sr_Latn.po @@ -7272,6 +7272,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/sv.po b/editor/translations/sv.po index 9ec654128a..f9e65bb600 100644 --- a/editor/translations/sv.po +++ b/editor/translations/sv.po @@ -2,24 +2,24 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # bergmarklund <davemcgroin@gmail.com>, 2017, 2018. # Christoffer Sundbom <christoffer_karlsson@live.se>, 2017. # Jakob Sinclair <sinclair.jakob@mailbox.org>, 2018. # . <grenoscar@gmail.com>, 2018. -# +# Kristoffer Grundström <kristoffer.grundstrom1983@gmail.com>, 2018. +# Magnus Helander <helander@fastmail.net>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-05-07 11:42+0000\n" -"Last-Translator: anonymous <>\n" +"PO-Revision-Date: 2018-07-24 12:44+0000\n" +"Last-Translator: Magnus Helander <helander@fastmail.net>\n" "Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/" "godot/sv/>\n" "Language: sv\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -30,9 +30,8 @@ msgid "All Selection" msgstr "Alla urval" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Time" -msgstr "Anim Ändra Värde" +msgstr "Anim Ändra Nyckelram Tid" #: editor/animation_editor.cpp msgid "Anim Change Transition" @@ -88,16 +87,15 @@ msgstr "Ändra Anim Spårets Värde Läge" #: editor/animation_editor.cpp msgid "Anim Track Change Wrap Mode" -msgstr "" +msgstr "Anim Spåra Ändra Linda om Läge" #: editor/animation_editor.cpp msgid "Edit Node Curve" msgstr "Redigera Nodkurva" #: editor/animation_editor.cpp -#, fuzzy msgid "Edit Selection Curve" -msgstr "Redigera Urval Kurva" +msgstr "Redigera Urvalsurva" #: editor/animation_editor.cpp msgid "Anim Delete Keys" @@ -7903,6 +7901,11 @@ msgstr "Projekt" msgid "Warnings" msgstr "Varning" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Visa Filer" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/ta.po b/editor/translations/ta.po index d7910c2c87..d3d80facc3 100644 --- a/editor/translations/ta.po +++ b/editor/translations/ta.po @@ -7274,6 +7274,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/th.po b/editor/translations/th.po index 4db8459f1b..2393ca98a9 100644 --- a/editor/translations/th.po +++ b/editor/translations/th.po @@ -7359,6 +7359,11 @@ msgstr "Build โปรเจกต์" msgid "Warnings" msgstr "คำเตือน" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "ดูไฟล์" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "สิ้นสุดสแตคข้อผิดพลาดภายใน" diff --git a/editor/translations/tr.po b/editor/translations/tr.po index 292cec4063..abe7d485fa 100644 --- a/editor/translations/tr.po +++ b/editor/translations/tr.po @@ -17,18 +17,19 @@ # razah <icnikerazah@gmail.com>, 2017-2018. # stnmycri <satenmeycri@gmail.com>, 2017-2018. # Yavuz Günay <yavuzgunay@gmail.com>, 2017. +# Onur Sanır <onursanir@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-06-10 09:46+0000\n" -"Last-Translator: Aykut YILDIRIM <aykutyildirim@windowslive.com>\n" +"PO-Revision-Date: 2018-07-07 20:42+0000\n" +"Last-Translator: Onur Sanır <onursanir@gmail.com>\n" "Language-Team: Turkish <https://hosted.weblate.org/projects/godot-engine/" "godot/tr/>\n" "Language: tr\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 3.0.1-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -5696,9 +5697,8 @@ msgid "Options" msgstr "Seçenekler" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Has,Many,Options" -msgstr "Bir Çok,Seçenek,Var!" +msgstr "Birçok,Seçenek,Var" #: editor/plugins/theme_editor_plugin.cpp msgid "Tab 1" @@ -7438,6 +7438,11 @@ msgstr "Projeyi İnşa et" msgid "Warnings" msgstr "Uyarılar" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Dosyaları Görüntüle" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "İç özel durum yığını izlemesinin sonu" diff --git a/editor/translations/uk.po b/editor/translations/uk.po index 067c7be724..d940561131 100644 --- a/editor/translations/uk.po +++ b/editor/translations/uk.po @@ -7447,6 +7447,11 @@ msgstr "Зібрати проект" msgid "Warnings" msgstr "Попередження" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "Перегляд файлів" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "Кінець трасування стека для внутрішнього виключення" diff --git a/editor/translations/ur_PK.po b/editor/translations/ur_PK.po index 0162eb0788..3857bff9b0 100644 --- a/editor/translations/ur_PK.po +++ b/editor/translations/ur_PK.po @@ -7331,6 +7331,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/vi.po b/editor/translations/vi.po index 6651bd170c..faf77300b8 100644 --- a/editor/translations/vi.po +++ b/editor/translations/vi.po @@ -2,25 +2,24 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # 01lifeleft <01lifeleft@gmail.com>, 2018. # Dlean Jeans <dleanjeans@gmail.com>, 2018. # Hai Le <dark.hades.1102@gmail.com>, 2017. # Nguyễn Tuấn Anh <anhnt.fami@gmail.com>, 2017. # Tung Le <tungkradle@gmail.com>, 2017. -# +# 38569459 <xxx38569459@gmail.com>, 2018. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-04-18 15:44+0000\n" -"Last-Translator: 01lifeleft <01lifeleft@gmail.com>\n" +"PO-Revision-Date: 2018-07-22 06:42+0000\n" +"Last-Translator: 38569459 <xxx38569459@gmail.com>\n" "Language-Team: Vietnamese <https://hosted.weblate.org/projects/godot-engine/" "godot/vi/>\n" "Language: vi\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -141,24 +140,24 @@ msgstr "Chọn Scale" #: editor/animation_editor.cpp msgid "Scale From Cursor" -msgstr "" +msgstr "Scale từ trỏ chuột" #: editor/animation_editor.cpp msgid "Goto Next Step" -msgstr "" +msgstr "Đến Step tiếp theo" #: editor/animation_editor.cpp msgid "Goto Prev Step" -msgstr "" +msgstr "Đến Step trước đó" #: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp #: editor/property_editor.cpp msgid "Linear" -msgstr "" +msgstr "Tuyến" #: editor/animation_editor.cpp editor/plugins/theme_editor_plugin.cpp msgid "Constant" -msgstr "" +msgstr "Cố định" #: editor/animation_editor.cpp msgid "In" @@ -178,11 +177,11 @@ msgstr "Ngoài-Trong" #: editor/animation_editor.cpp msgid "Transitions" -msgstr "" +msgstr "Chuyển tiếp" #: editor/animation_editor.cpp msgid "Optimize Animation" -msgstr "" +msgstr "Tối ưu Animation" #: editor/animation_editor.cpp msgid "Clean-Up Animation" @@ -190,11 +189,11 @@ msgstr "Dọn dẹp Animation" #: editor/animation_editor.cpp msgid "Create NEW track for %s and insert key?" -msgstr "" +msgstr "Tạo track mới cho %s và chèn key?" #: editor/animation_editor.cpp msgid "Create %d NEW tracks and insert keys?" -msgstr "" +msgstr "Tạo %d track mới và chèn key?" #: editor/animation_editor.cpp editor/create_dialog.cpp #: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp @@ -210,15 +209,15 @@ msgstr "Tạo & Chèn Anim" #: editor/animation_editor.cpp msgid "Anim Insert Track & Key" -msgstr "" +msgstr "Chèn Track & Key Anim" #: editor/animation_editor.cpp msgid "Anim Insert Key" -msgstr "" +msgstr "Chèn Key Anim" #: editor/animation_editor.cpp msgid "Change Anim Len" -msgstr "" +msgstr "Đổi độ dài Anim" #: editor/animation_editor.cpp msgid "Change Anim Loop" @@ -226,23 +225,25 @@ msgstr "Đổi vòng lặp Anim" #: editor/animation_editor.cpp msgid "Anim Create Typed Value Key" -msgstr "" +msgstr "Tạo Key để nhập giá trị Anim" #: editor/animation_editor.cpp msgid "Anim Insert" msgstr "Chèn Anim" #: editor/animation_editor.cpp +#, fuzzy msgid "Anim Scale Keys" -msgstr "" +msgstr "Anim Scale Keys" #: editor/animation_editor.cpp msgid "Anim Add Call Track" msgstr "Thêm Track Gọi Function" #: editor/animation_editor.cpp +#, fuzzy msgid "Animation zoom." -msgstr "" +msgstr "Phóng Animation." #: editor/animation_editor.cpp msgid "Length (s):" @@ -266,27 +267,27 @@ msgstr "Mở/Tắt lặp animation." #: editor/animation_editor.cpp msgid "Add new tracks." -msgstr "" +msgstr "Thêm track mới." #: editor/animation_editor.cpp msgid "Move current track up." -msgstr "" +msgstr "Di chuyển track lên." #: editor/animation_editor.cpp msgid "Move current track down." -msgstr "" +msgstr "Di chuyển track xuống." #: editor/animation_editor.cpp msgid "Remove selected track." -msgstr "" +msgstr "Bỏ track đang chọn." #: editor/animation_editor.cpp msgid "Track tools" -msgstr "" +msgstr "Công cụ Track" #: editor/animation_editor.cpp msgid "Enable editing of individual keys by clicking them." -msgstr "" +msgstr "Cho phép chỉnh sửa từng key riêng bằng cách chọn chúng." #: editor/animation_editor.cpp msgid "Anim. Optimizer" @@ -306,162 +307,171 @@ msgstr "" #: editor/animation_editor.cpp msgid "Optimize" -msgstr "" +msgstr "Tối ưu" #: editor/animation_editor.cpp msgid "Select an AnimationPlayer from the Scene Tree to edit animations." msgstr "Chọn một AnimationPlayer từ Scene Tree để chỉnh sửa animation." #: editor/animation_editor.cpp +#, fuzzy msgid "Key" -msgstr "" +msgstr "Mã" #: editor/animation_editor.cpp msgid "Transition" -msgstr "" +msgstr "Chuyển tiếp" #: editor/animation_editor.cpp msgid "Scale Ratio:" -msgstr "" +msgstr "Tỉ lệ Scale:" #: editor/animation_editor.cpp msgid "Call Functions in Which Node?" -msgstr "" +msgstr "Gọi Function từ Node nào?" #: editor/animation_editor.cpp msgid "Remove invalid keys" -msgstr "" +msgstr "Hủy key không đúng chuẩn" #: editor/animation_editor.cpp +#, fuzzy msgid "Remove unresolved and empty tracks" -msgstr "" +msgstr "Gỡ bỏ track trống và không tìm thấy" #: editor/animation_editor.cpp msgid "Clean-up all animations" -msgstr "" +msgstr "Dọn dẹp tất cả animations" #: editor/animation_editor.cpp msgid "Clean-Up Animation(s) (NO UNDO!)" -msgstr "" +msgstr "Dọn dẹp tất cả Animation (KHÔNG THỂ HỒI LẠI)" #: editor/animation_editor.cpp msgid "Clean-Up" -msgstr "" +msgstr "Dọn dẹp" #: editor/array_property_edit.cpp msgid "Resize Array" -msgstr "" +msgstr "Đổi lại size Array" #: editor/array_property_edit.cpp msgid "Change Array Value Type" -msgstr "" +msgstr "Đổi loại giá trị Array" #: editor/array_property_edit.cpp msgid "Change Array Value" -msgstr "" +msgstr "Đổi giá trị Array" #: editor/code_editor.cpp msgid "Go to Line" -msgstr "" +msgstr "Đến Dòng" #: editor/code_editor.cpp msgid "Line Number:" -msgstr "" +msgstr "Dòng số:" #: editor/code_editor.cpp msgid "No Matches" -msgstr "" +msgstr "Không tìm thấy" #: editor/code_editor.cpp msgid "Replaced %d occurrence(s)." msgstr "" #: editor/code_editor.cpp +#, fuzzy msgid "Match Case" -msgstr "" +msgstr "Trùng khớp" #: editor/code_editor.cpp msgid "Whole Words" -msgstr "" +msgstr "Cả từ" #: editor/code_editor.cpp msgid "Replace" -msgstr "" +msgstr "Thay thế" #: editor/code_editor.cpp msgid "Replace All" -msgstr "" +msgstr "Thay thế tất cả" #: editor/code_editor.cpp msgid "Selection Only" -msgstr "" +msgstr "Chỉ lựa chọn" #: editor/code_editor.cpp msgid "Zoom In" -msgstr "" +msgstr "Phóng to" #: editor/code_editor.cpp msgid "Zoom Out" -msgstr "" +msgstr "Thu nhỏ" #: editor/code_editor.cpp msgid "Reset Zoom" -msgstr "" +msgstr "Đặt lại phóng" #: editor/code_editor.cpp editor/script_editor_debugger.cpp msgid "Line:" -msgstr "" +msgstr "Dòng:" #: editor/code_editor.cpp +#, fuzzy msgid "Col:" -msgstr "" +msgstr "Col:" #: editor/connections_dialog.cpp msgid "Method in target Node must be specified!" -msgstr "" +msgstr "Cách thức trong Node được chọn phải được ghi rõ!" #: editor/connections_dialog.cpp msgid "" "Target method not found! Specify a valid method or attach a script to target " "Node." msgstr "" +"Cách thức của đối tượng không tìm thấy! ghi rõ một cách thức hợp lệ hoặc " +"đính kèm một script cho đối tượng Node." #: editor/connections_dialog.cpp msgid "Connect To Node:" -msgstr "" +msgstr "Kết nối đến Node:" #: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp #: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp #: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp msgid "Add" -msgstr "" +msgstr "Thêm" #: editor/connections_dialog.cpp editor/dependency_editor.cpp #: editor/plugins/animation_tree_editor_plugin.cpp #: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp #: editor/project_settings_editor.cpp msgid "Remove" -msgstr "" +msgstr "Xóa" #: editor/connections_dialog.cpp +#, fuzzy msgid "Add Extra Call Argument:" -msgstr "" +msgstr "Thêm đối số:" #: editor/connections_dialog.cpp msgid "Extra Call Arguments:" msgstr "" #: editor/connections_dialog.cpp +#, fuzzy msgid "Path to Node:" -msgstr "" +msgstr "Đường đến Node:" #: editor/connections_dialog.cpp msgid "Make Function" -msgstr "" +msgstr "Tạo Function" #: editor/connections_dialog.cpp +#, fuzzy msgid "Deferred" -msgstr "" +msgstr "Hoãn lại" #: editor/connections_dialog.cpp msgid "Oneshot" @@ -479,45 +489,45 @@ msgstr "" #: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp #: modules/visual_script/visual_script_editor.cpp msgid "Close" -msgstr "" +msgstr "Tắt" #: editor/connections_dialog.cpp msgid "Connect" -msgstr "" +msgstr "Kết nối" #: editor/connections_dialog.cpp msgid "Connect '%s' to '%s'" -msgstr "" +msgstr "Kết nối '%s' đến '%s'" #: editor/connections_dialog.cpp msgid "Connecting Signal:" -msgstr "" +msgstr "Đang kết nối Signal:" #: editor/connections_dialog.cpp msgid "Disconnect '%s' from '%s'" -msgstr "" +msgstr "Hủy kết nối '%s' từ '%s'" #: editor/connections_dialog.cpp msgid "Connect..." -msgstr "" +msgstr "Kết nối..." #: editor/connections_dialog.cpp #: editor/plugins/animation_tree_editor_plugin.cpp msgid "Disconnect" -msgstr "" +msgstr "Hủy kết nối" #: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp msgid "Signals" -msgstr "" +msgstr "Tín hiệu" #: editor/create_dialog.cpp msgid "Change %s Type" -msgstr "" +msgstr "Đổi %s Type" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp msgid "Change" -msgstr "" +msgstr "Đổi" #: editor/create_dialog.cpp msgid "Create New %s" @@ -526,38 +536,39 @@ msgstr "Tạo %s Mới" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp msgid "Favorites:" -msgstr "" +msgstr "Ưa thích:" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp msgid "Recent:" -msgstr "" +msgstr "Gần đây:" #: editor/create_dialog.cpp editor/editor_node.cpp #: editor/plugins/asset_library_editor_plugin.cpp #: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp #: editor/quick_open.cpp msgid "Search:" -msgstr "" +msgstr "Tìm kiếm:" #: editor/create_dialog.cpp editor/editor_help.cpp #: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp #: editor/quick_open.cpp msgid "Matches:" -msgstr "" +msgstr "Phù hợp:" #: editor/create_dialog.cpp editor/editor_help.cpp #: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp #: editor/script_editor_debugger.cpp msgid "Description:" -msgstr "" +msgstr "Mô tả:" #: editor/dependency_editor.cpp msgid "Search Replacement For:" -msgstr "" +msgstr "Tìm kiếm thay thế cho:" #: editor/dependency_editor.cpp +#, fuzzy msgid "Dependencies For:" -msgstr "" +msgstr "Phần phụ thuộc cho:" #: editor/dependency_editor.cpp msgid "" @@ -2017,12 +2028,14 @@ msgid "Issue Tracker" msgstr "" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp +#, fuzzy msgid "Community" -msgstr "" +msgstr "Cộng đồng" #: editor/editor_node.cpp +#, fuzzy msgid "About" -msgstr "" +msgstr "Thông tin" #: editor/editor_node.cpp msgid "Play the project." @@ -4223,7 +4236,7 @@ msgstr "" #: editor/plugins/particles_editor_plugin.cpp msgid "Volume" -msgstr "" +msgstr "Âm lượng" #: editor/plugins/particles_editor_plugin.cpp msgid "Emission Source: " @@ -4265,7 +4278,7 @@ msgstr "" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp msgid "Select Points" -msgstr "" +msgstr "Chọn Points" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp @@ -4275,12 +4288,12 @@ msgstr "" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp msgid "Click: Add Point" -msgstr "" +msgstr "Nhấp: Tạo Point" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp msgid "Right Click: Delete Point" -msgstr "" +msgstr "Nhấp chuột phải: Xóa Point" #: editor/plugins/path_2d_editor_plugin.cpp msgid "Select Control Points (Shift+Drag)" @@ -4299,7 +4312,7 @@ msgstr "" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp msgid "Delete Point" -msgstr "" +msgstr "Xóa Point" #: editor/plugins/path_2d_editor_plugin.cpp #: editor/plugins/path_editor_plugin.cpp @@ -4562,27 +4575,27 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme" -msgstr "" +msgstr "Lưu Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme As" -msgstr "" +msgstr "Lưu Theme thành" #: editor/plugins/script_editor_plugin.cpp msgid "Close Docs" -msgstr "" +msgstr "Đóng Docs" #: editor/plugins/script_editor_plugin.cpp msgid "Close All" -msgstr "" +msgstr "Đóng tất cả" #: editor/plugins/script_editor_plugin.cpp msgid "Close Other Tabs" -msgstr "" +msgstr "Đóng tất cả Tab" #: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp msgid "Run" -msgstr "" +msgstr "Chạy" #: editor/plugins/script_editor_plugin.cpp msgid "Toggle Scripts Panel" @@ -4591,12 +4604,12 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp msgid "Find..." -msgstr "" +msgstr "Tìm..." #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/script_text_editor.cpp msgid "Find Next" -msgstr "" +msgstr "Tìm tiếp theo" #: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp msgid "Step Over" @@ -4613,7 +4626,7 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp #: editor/script_editor_debugger.cpp msgid "Continue" -msgstr "" +msgstr "Tiếp tục" #: editor/plugins/script_editor_plugin.cpp msgid "Keep Debugger Open" @@ -4645,11 +4658,11 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp msgid "Discard" -msgstr "" +msgstr "Hủy" #: editor/plugins/script_editor_plugin.cpp msgid "Create Script" -msgstr "" +msgstr "Tạo Script" #: editor/plugins/script_editor_plugin.cpp msgid "" @@ -4680,7 +4693,7 @@ msgstr "" #: editor/plugins/script_text_editor.cpp msgid "Pick Color" -msgstr "" +msgstr "Chọn màu" #: editor/plugins/script_text_editor.cpp msgid "Convert Case" @@ -4701,13 +4714,13 @@ msgstr "" #: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp #: scene/gui/text_edit.cpp msgid "Cut" -msgstr "" +msgstr "Cắt" #: editor/plugins/script_text_editor.cpp #: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp #: scene/gui/line_edit.cpp scene/gui/text_edit.cpp msgid "Copy" -msgstr "" +msgstr "Copy" #: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp #: scene/gui/text_edit.cpp @@ -6252,8 +6265,9 @@ msgid "Project Settings (project.godot)" msgstr "" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp +#, fuzzy msgid "General" -msgstr "" +msgstr "Tổng quan" #: editor/project_settings_editor.cpp editor/property_editor.cpp msgid "Property:" @@ -7283,6 +7297,10 @@ msgstr "" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +msgid "View log" +msgstr "" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po index 48e30ceab3..51e0181fc8 100644 --- a/editor/translations/zh_CN.po +++ b/editor/translations/zh_CN.po @@ -7372,6 +7372,11 @@ msgstr "构建项目" msgid "Warnings" msgstr "警告" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "查看文件" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "内部异常堆栈追朔结束" diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po index 568390a7a8..de03512af1 100644 --- a/editor/translations/zh_HK.po +++ b/editor/translations/zh_HK.po @@ -7589,6 +7589,11 @@ msgstr "專案" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "檔案" + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po index 38b565a37f..df0c474322 100644 --- a/editor/translations/zh_TW.po +++ b/editor/translations/zh_TW.po @@ -2,7 +2,6 @@ # Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. # Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) # This file is distributed under the same license as the Godot source code. -# # Allen H <w84miracle@gmail.com>, 2017. # Billy SU <g4691821@gmail.com>, 2018. # Chao Yu <casd82@gmail.com>, 2017. @@ -12,19 +11,18 @@ # popcade <popcade@gmail.com>, 2016. # Qing <icinriiq@gmail.com>, 2018. # Sam Pan <sampan66@gmail.com>, 2016. -# msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2018-04-24 09:35+0000\n" -"Last-Translator: Qing <icinriiq@gmail.com>\n" +"PO-Revision-Date: 2018-07-15 16:35+0000\n" +"Last-Translator: Kisaragi Hiu <mail@kisaragi-hiu.com>\n" "Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hant/>\n" "Language: zh_TW\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 3.0-dev\n" +"X-Generator: Weblate 3.1-dev\n" #: editor/animation_editor.cpp msgid "Disabled" @@ -35,9 +33,8 @@ msgid "All Selection" msgstr "所有的選擇" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Keyframe Time" -msgstr "動畫更改座標" +msgstr "動畫更改關鍵幀時間" #: editor/animation_editor.cpp msgid "Anim Change Transition" @@ -52,9 +49,8 @@ msgid "Anim Change Keyframe Value" msgstr "動畫更改關鍵幀數值" #: editor/animation_editor.cpp -#, fuzzy msgid "Anim Change Call" -msgstr "動畫改變呼叫" +msgstr "動畫更改呼叫" #: editor/animation_editor.cpp msgid "Anim Add Track" @@ -345,7 +341,7 @@ msgstr "移除無效按鍵" #: editor/animation_editor.cpp msgid "Remove unresolved and empty tracks" -msgstr "" +msgstr "刪除未解決或是空的軌道" #: editor/animation_editor.cpp msgid "Clean-up all animations" @@ -523,9 +519,8 @@ msgid "Signals" msgstr "信號" #: editor/create_dialog.cpp -#, fuzzy msgid "Change %s Type" -msgstr "變更鏡頭尺寸" +msgstr "變更 %s 尺寸" #: editor/create_dialog.cpp editor/project_settings_editor.cpp #: modules/visual_script/visual_script_editor.cpp @@ -533,9 +528,8 @@ msgid "Change" msgstr "更換" #: editor/create_dialog.cpp -#, fuzzy msgid "Create New %s" -msgstr "新增" +msgstr "新增 %s" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -618,7 +612,7 @@ msgstr "相依性編輯器" #: editor/dependency_editor.cpp msgid "Search Replacement Resource:" -msgstr "" +msgstr "搜尋替代資源:" #: editor/dependency_editor.cpp editor/editor_file_dialog.cpp #: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp @@ -670,7 +664,7 @@ msgstr "修復相依性" #: editor/dependency_editor.cpp msgid "Errors loading!" -msgstr "" +msgstr "載入錯誤!" #: editor/dependency_editor.cpp msgid "Permanently delete %d item(s)? (No undo!)" @@ -682,7 +676,7 @@ msgstr "擁有" #: editor/dependency_editor.cpp msgid "Resources Without Explicit Ownership:" -msgstr "" +msgstr "沒有明定擁有者的資源:" #: editor/dependency_editor.cpp editor/editor_node.cpp msgid "Orphan Resource Explorer" @@ -701,16 +695,19 @@ msgid "Delete" msgstr "刪除" #: editor/dictionary_property_edit.cpp +#, fuzzy msgid "Change Dictionary Key" -msgstr "" +msgstr "改變字典 key" #: editor/dictionary_property_edit.cpp +#, fuzzy msgid "Change Dictionary Value" -msgstr "" +msgstr "改變字典 value" #: editor/editor_about.cpp +#, fuzzy msgid "Thanks from the Godot community!" -msgstr "" +msgstr "Godot 社群感謝你!" #: editor/editor_about.cpp msgid "Thanks!" @@ -718,7 +715,7 @@ msgstr "謝謝!" #: editor/editor_about.cpp msgid "Godot Engine contributors" -msgstr "" +msgstr "Godot Engine 貢獻者" #: editor/editor_about.cpp msgid "Project Founders" @@ -726,12 +723,11 @@ msgstr "專案創始人" #: editor/editor_about.cpp msgid "Lead Developer" -msgstr "" +msgstr "主開發者" #: editor/editor_about.cpp -#, fuzzy msgid "Project Manager " -msgstr "專案創始人" +msgstr "專案管理人 " #: editor/editor_about.cpp msgid "Developers" @@ -739,35 +735,38 @@ msgstr "開發者" #: editor/editor_about.cpp msgid "Authors" -msgstr "" +msgstr "作者" #: editor/editor_about.cpp msgid "Platinum Sponsors" -msgstr "" +msgstr "白金贊助" #: editor/editor_about.cpp msgid "Gold Sponsors" -msgstr "" +msgstr "黃金贊助" #: editor/editor_about.cpp +#, fuzzy msgid "Mini Sponsors" -msgstr "" +msgstr "迷你贊助" #: editor/editor_about.cpp msgid "Gold Donors" -msgstr "" +msgstr "黃金捐贈者" #: editor/editor_about.cpp +#, fuzzy msgid "Silver Donors" -msgstr "" +msgstr "白銀捐贈者" #: editor/editor_about.cpp +#, fuzzy msgid "Bronze Donors" -msgstr "" +msgstr "紅銅捐贈者" #: editor/editor_about.cpp msgid "Donors" -msgstr "" +msgstr "捐贈者" #: editor/editor_about.cpp msgid "License" @@ -775,7 +774,7 @@ msgstr "授權" #: editor/editor_about.cpp msgid "Thirdparty License" -msgstr "" +msgstr "第三方授權條款" #: editor/editor_about.cpp msgid "" @@ -784,14 +783,16 @@ msgid "" "is an exhaustive list of all such thirdparty components with their " "respective copyright statements and license terms." msgstr "" +"Godot Engine 依賴著許多與 MIT 授權條款相容、自由開源的第三方函式庫。以下是這" +"些第三方元件的完整列表,附有它們各自的著作權宣示與授權條款。" #: editor/editor_about.cpp msgid "All Components" -msgstr "" +msgstr "所有元件" #: editor/editor_about.cpp msgid "Components" -msgstr "" +msgstr "元件" #: editor/editor_about.cpp msgid "Licenses" @@ -799,21 +800,20 @@ msgstr "授權" #: editor/editor_asset_installer.cpp editor/project_manager.cpp msgid "Error opening package file, not in zip format." -msgstr "" +msgstr "開啟套件檔案出錯,非 zip 格式。" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Uncompressing Assets" -msgstr "(重新)載入素材" +msgstr "正在解壓縮素材" #: editor/editor_asset_installer.cpp editor/project_manager.cpp msgid "Package Installed Successfully!" -msgstr "" +msgstr "套件安裝成功!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Success!" -msgstr "" +msgstr "成功!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -821,147 +821,148 @@ msgid "Install" msgstr "安裝" #: editor/editor_asset_installer.cpp +#, fuzzy msgid "Package Installer" -msgstr "" +msgstr "套件安裝" #: editor/editor_audio_buses.cpp msgid "Speakers" -msgstr "" +msgstr "喇叭" #: editor/editor_audio_buses.cpp msgid "Add Effect" -msgstr "" +msgstr "新增效果" #: editor/editor_audio_buses.cpp msgid "Rename Audio Bus" -msgstr "" +msgstr "重新命名 Audio Bus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Change Audio Bus Volume" -msgstr "重設縮放大小" +msgstr "變更 Audio Bus 音量" #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Toggle Audio Bus Solo" -msgstr "" +msgstr "切換 Audio Bus 的 Solo" #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Toggle Audio Bus Mute" -msgstr "" +msgstr "切換 Audio Bus 的 Mute" #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Toggle Audio Bus Bypass Effects" -msgstr "" +msgstr "切換 Audio Bus 忽略效果" #: editor/editor_audio_buses.cpp msgid "Select Audio Bus Send" -msgstr "" +msgstr "選擇 Audio Bus 輸出地點" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus Effect" -msgstr "" +msgstr "新增 Audio Bus 效果" #: editor/editor_audio_buses.cpp msgid "Move Bus Effect" -msgstr "" +msgstr "移動 Bus 效果" #: editor/editor_audio_buses.cpp msgid "Delete Bus Effect" -msgstr "" +msgstr "刪除 Bus 效果" #: editor/editor_audio_buses.cpp msgid "Audio Bus, Drag and Drop to rearrange." -msgstr "" +msgstr "Audio Bus。拖放以重新排列。" #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Solo" -msgstr "" +msgstr "Solo" #: editor/editor_audio_buses.cpp msgid "Mute" msgstr "靜音" #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Bypass" -msgstr "" +msgstr "忽略效果 (Bypass)" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus options" -msgstr "除錯選項" +msgstr "Bus 選項" #: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp #: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp msgid "Duplicate" -msgstr "" +msgstr "製作複本" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Reset Volume" -msgstr "重設縮放大小" +msgstr "重設音量" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Delete Effect" -msgstr "刪除" +msgstr "刪除效果" #: editor/editor_audio_buses.cpp msgid "Audio" -msgstr "" +msgstr "聲音" #: editor/editor_audio_buses.cpp msgid "Add Audio Bus" -msgstr "" +msgstr "新增 Audio Bus" #: editor/editor_audio_buses.cpp msgid "Master bus can't be deleted!" -msgstr "" +msgstr "Master Bus 不能被刪除!" #: editor/editor_audio_buses.cpp msgid "Delete Audio Bus" -msgstr "" +msgstr "刪除 Audio Bus" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Duplicate Audio Bus" -msgstr "複製所選" +msgstr "製作 Audio Bus 的複本" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Reset Bus Volume" -msgstr "重設縮放大小" +msgstr "重設 Bus 音量" #: editor/editor_audio_buses.cpp msgid "Move Audio Bus" -msgstr "" +msgstr "移動 Audio Bus" #: editor/editor_audio_buses.cpp msgid "Save Audio Bus Layout As..." -msgstr "" +msgstr "另存 Audio Bus 配置為..." #: editor/editor_audio_buses.cpp +#, fuzzy msgid "Location for New Layout..." -msgstr "" +msgstr "新配置的位置..." #: editor/editor_audio_buses.cpp msgid "Open Audio Bus Layout" -msgstr "" +msgstr "開啟 Audio Bus 配置" #: editor/editor_audio_buses.cpp msgid "There is no 'res://default_bus_layout.tres' file." -msgstr "" +msgstr "「res://default_bus_layout.tres」檔案不存在。" #: editor/editor_audio_buses.cpp msgid "Invalid file, not an audio bus layout." -msgstr "" +msgstr "檔案格式不正確,不是 Audio Bus 配置檔。" #: editor/editor_audio_buses.cpp msgid "Add Bus" -msgstr "" +msgstr "新增 Bus" #: editor/editor_audio_buses.cpp msgid "Create a new Bus Layout." -msgstr "" +msgstr "建立新的 Bus 配置。" #: editor/editor_audio_buses.cpp editor/property_editor.cpp #: editor/script_create_dialog.cpp @@ -970,7 +971,7 @@ msgstr "載入" #: editor/editor_audio_buses.cpp msgid "Load an existing Bus Layout." -msgstr "" +msgstr "讀取現存的 Bus 配置。" #: editor/editor_audio_buses.cpp #: editor/plugins/animation_player_editor_plugin.cpp @@ -979,7 +980,7 @@ msgstr "另存新檔" #: editor/editor_audio_buses.cpp msgid "Save this Bus Layout to a file." -msgstr "" +msgstr "儲存目前的 Bus 配置到檔案裡。" #: editor/editor_audio_buses.cpp editor/import_dock.cpp msgid "Load Default" @@ -987,7 +988,7 @@ msgstr "載入預設值" #: editor/editor_audio_buses.cpp msgid "Load the default Bus Layout." -msgstr "" +msgstr "載入預設的 Bus 配置。" #: editor/editor_autoload_settings.cpp msgid "Invalid name." @@ -999,7 +1000,7 @@ msgstr "合法字元:" #: editor/editor_autoload_settings.cpp msgid "Invalid name. Must not collide with an existing engine class name." -msgstr "" +msgstr "不正確的名字。名字不能與現有的 engine class 名衝突。" #: editor/editor_autoload_settings.cpp msgid "Invalid name. Must not collide with an existing buit-in type name." @@ -1022,16 +1023,19 @@ msgid "Not in resource path." msgstr "在資源路徑中找不到" #: editor/editor_autoload_settings.cpp +#, fuzzy msgid "Add AutoLoad" -msgstr "" +msgstr "新增 AutoLoad" #: editor/editor_autoload_settings.cpp +#, fuzzy msgid "Autoload '%s' already exists!" -msgstr "" +msgstr "Autoload「%s」已經存在!" #: editor/editor_autoload_settings.cpp +#, fuzzy msgid "Rename Autoload" -msgstr "" +msgstr "重新命名 Autoload" #: editor/editor_autoload_settings.cpp msgid "Toggle AutoLoad Globals" @@ -1039,19 +1043,21 @@ msgstr "" #: editor/editor_autoload_settings.cpp msgid "Move Autoload" -msgstr "" +msgstr "移動 Autoload" #: editor/editor_autoload_settings.cpp +#, fuzzy msgid "Remove Autoload" -msgstr "" +msgstr "刪除 Autoload" #: editor/editor_autoload_settings.cpp msgid "Enable" msgstr "啟用" #: editor/editor_autoload_settings.cpp +#, fuzzy msgid "Rearrange Autoloads" -msgstr "" +msgstr "重新排列 Autoload" #: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp #: scene/gui/file_dialog.cpp @@ -1084,16 +1090,18 @@ msgid "Updating scene..." msgstr "更新場景中..." #: editor/editor_data.cpp +#, fuzzy msgid "[empty]" -msgstr "" +msgstr "(空)" #: editor/editor_data.cpp msgid "[unsaved]" -msgstr "" +msgstr "(未儲存)" #: editor/editor_dir_dialog.cpp +#, fuzzy msgid "Please select a base directory first" -msgstr "" +msgstr "請先選擇一個基底的資料夾" #: editor/editor_dir_dialog.cpp msgid "Choose a Directory" @@ -1122,7 +1130,7 @@ msgstr "選擇" #: editor/editor_export.cpp msgid "Storing File:" -msgstr "" +msgstr "儲存檔案:" #: editor/editor_export.cpp msgid "Packing" @@ -1137,22 +1145,20 @@ msgid "File Exists, Overwrite?" msgstr "檔案已經存在, 要覆寫嗎?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "Select Current Folder" -msgstr "新增資料夾" +msgstr "選擇目前的資料夾" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Copy Path" -msgstr "" +msgstr "複製路徑" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "Show In File Manager" -msgstr "" +msgstr "在檔案管理員內顯示" #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp -#, fuzzy msgid "New Folder..." -msgstr "新增資料夾" +msgstr "新增資料夾..." #: editor/editor_file_dialog.cpp msgid "Refresh" @@ -1160,7 +1166,7 @@ msgstr "重新整理" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "All Recognized" -msgstr "" +msgstr "可認得全部" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "All Files (*)" @@ -1168,7 +1174,7 @@ msgstr "所有類型檔案" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open a File" -msgstr "" +msgstr "開啟檔案" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Open File(s)" @@ -1190,7 +1196,7 @@ msgstr "儲存" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Save a File" -msgstr "" +msgstr "儲存檔案" #: editor/editor_file_dialog.cpp msgid "Go Back" @@ -1209,12 +1215,13 @@ msgid "Toggle Hidden Files" msgstr "切換顯示隱藏檔案" #: editor/editor_file_dialog.cpp +#, fuzzy msgid "Toggle Favorite" -msgstr "" +msgstr "切換最愛" #: editor/editor_file_dialog.cpp msgid "Toggle Mode" -msgstr "" +msgstr "切換模式" #: editor/editor_file_dialog.cpp msgid "Focus Path" @@ -1247,8 +1254,9 @@ msgid "File:" msgstr "檔案:" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp +#, fuzzy msgid "Must use a valid extension." -msgstr "" +msgstr "必須使用有效的副檔名。" #: editor/editor_file_system.cpp msgid "ScanSources" @@ -1261,27 +1269,28 @@ msgstr "(重新)載入素材" #: editor/editor_help.cpp editor/editor_node.cpp #: editor/plugins/script_editor_plugin.cpp msgid "Search Help" -msgstr "" +msgstr "搜尋幫助" #: editor/editor_help.cpp msgid "Class List:" -msgstr "" +msgstr "Class 列表:" #: editor/editor_help.cpp msgid "Search Classes" -msgstr "" +msgstr "搜尋 Class" #: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp +#, fuzzy msgid "Top" -msgstr "" +msgstr "上面" #: editor/editor_help.cpp editor/property_editor.cpp msgid "Class:" -msgstr "" +msgstr "Class:" #: editor/editor_help.cpp editor/scene_tree_editor.cpp msgid "Inherits:" -msgstr "" +msgstr "繼承:" #: editor/editor_help.cpp msgid "Inherited by:" @@ -1305,19 +1314,19 @@ msgstr "" #: editor/editor_help.cpp msgid "Public Methods:" -msgstr "" +msgstr "公開 method:" #: editor/editor_help.cpp msgid "GUI Theme Items" -msgstr "" +msgstr "介面主題項目" #: editor/editor_help.cpp msgid "GUI Theme Items:" -msgstr "" +msgstr "介面主題項目:" #: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp msgid "Signals:" -msgstr "" +msgstr "訊號:" #: editor/editor_help.cpp msgid "Enumerations" @@ -1333,11 +1342,11 @@ msgstr "" #: editor/editor_help.cpp msgid "Constants" -msgstr "" +msgstr "定數" #: editor/editor_help.cpp msgid "Constants:" -msgstr "" +msgstr "定數:" #: editor/editor_help.cpp msgid "Description" @@ -1345,7 +1354,7 @@ msgstr "描述:" #: editor/editor_help.cpp msgid "Online Tutorials:" -msgstr "" +msgstr "線上教學:" #: editor/editor_help.cpp msgid "" @@ -1353,20 +1362,25 @@ msgid "" "$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/" "url][/color]." msgstr "" +"目前沒有這個 class 的教學,你可以[color=$color][url=$url]貢獻一個[/url][/" +"color]或[color=$color][url=$url2]要求一個[/url][/color]。" #: editor/editor_help.cpp msgid "Properties" msgstr "" #: editor/editor_help.cpp +#, fuzzy msgid "Property Description:" -msgstr "" +msgstr "Property 說明:" #: editor/editor_help.cpp msgid "" "There is currently no description for this property. Please help us by " "[color=$color][url=$url]contributing one[/url][/color]!" msgstr "" +"目前沒有這個 property 的說明。請幫我們[color=$color][url=$url]貢獻[/url][/" +"color]一個!" #: editor/editor_help.cpp msgid "Methods" @@ -1374,13 +1388,15 @@ msgstr "方法" #: editor/editor_help.cpp msgid "Method Description:" -msgstr "" +msgstr "Method 說明:" #: editor/editor_help.cpp msgid "" "There is currently no description for this method. Please help us by [color=" "$color][url=$url]contributing one[/url][/color]!" msgstr "" +"目前沒有這個 method 的說明。請幫我們[color=$color][url=$url]貢獻[/url][/" +"color]一個!" #: editor/editor_help.cpp msgid "Search Text" @@ -1408,15 +1424,15 @@ msgstr "輸出:" #: editor/editor_node.cpp msgid "Project export failed with error code %d." -msgstr "" +msgstr "專案輸出失敗,錯誤代碼是 %d。" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Error saving resource!" -msgstr "" +msgstr "儲存資源錯誤!" #: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp msgid "Save Resource As..." -msgstr "" +msgstr "另存資源為..." #: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp #: editor/scene_tree_dock.cpp @@ -1424,16 +1440,17 @@ msgid "I see..." msgstr "我知道了" #: editor/editor_node.cpp +#, fuzzy msgid "Can't open file for writing:" -msgstr "" +msgstr "無法以寫入模式開啟檔案:" #: editor/editor_node.cpp msgid "Requested file format unknown:" -msgstr "" +msgstr "要求了不明的檔案格式:" #: editor/editor_node.cpp msgid "Error while saving." -msgstr "" +msgstr "儲存中發生了錯誤。" #: editor/editor_node.cpp msgid "Can't open '%s'." @@ -1445,11 +1462,12 @@ msgstr "分析 \"%s\" 時發生錯誤。" #: editor/editor_node.cpp msgid "Unexpected end of file '%s'." -msgstr "" +msgstr "意料外的檔案結尾 (EOF) '%s'。" #: editor/editor_node.cpp +#, fuzzy msgid "Missing '%s' or its dependencies." -msgstr "" +msgstr "缺失 '%s' 或它的依存。" #: editor/editor_node.cpp msgid "Error while loading '%s'." @@ -1457,7 +1475,7 @@ msgstr "載入 \"%s\" 時發生錯誤。" #: editor/editor_node.cpp msgid "Saving Scene" -msgstr "" +msgstr "正在儲存場景" #: editor/editor_node.cpp msgid "Analyzing" @@ -7424,6 +7442,11 @@ msgstr "專案設定" msgid "Warnings" msgstr "" +#: modules/mono/editor/mono_bottom_panel.cpp +#, fuzzy +msgid "View log" +msgstr "過濾檔案..." + #: modules/mono/mono_gd/gd_mono_utils.cpp msgid "End of inner exception stack trace" msgstr "" diff --git a/gles_builders.py b/gles_builders.py new file mode 100644 index 0000000000..b5a2b24aa3 --- /dev/null +++ b/gles_builders.py @@ -0,0 +1,510 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main + + +class LegacyGLHeaderStruct: + + def __init__(self): + self.vertex_lines = [] + self.fragment_lines = [] + self.uniforms = [] + self.attributes = [] + self.feedbacks = [] + self.fbos = [] + self.conditionals = [] + self.enums = {} + self.texunits = [] + self.texunit_names = [] + self.ubos = [] + self.ubo_names = [] + + self.vertex_included_files = [] + self.fragment_included_files = [] + + self.reading = "" + self.line_offset = 0 + self.vertex_offset = 0 + self.fragment_offset = 0 + + +def include_file_in_legacygl_header(filename, header_data, depth): + fs = open(filename, "r") + line = fs.readline() + + while line: + + if line.find("[vertex]") != -1: + header_data.reading = "vertex" + line = fs.readline() + header_data.line_offset += 1 + header_data.vertex_offset = header_data.line_offset + continue + + if line.find("[fragment]") != -1: + header_data.reading = "fragment" + line = fs.readline() + header_data.line_offset += 1 + header_data.fragment_offset = header_data.line_offset + continue + + while line.find("#include ") != -1: + includeline = line.replace("#include ", "").strip()[1:-1] + + import os.path + + included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) + if not included_file in header_data.vertex_included_files and header_data.reading == "vertex": + header_data.vertex_included_files += [included_file] + if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None: + print("Error in file '" + filename + "': #include " + includeline + "could not be found!") + elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment": + header_data.fragment_included_files += [included_file] + if include_file_in_legacygl_header(included_file, header_data, depth + 1) == None: + print("Error in file '" + filename + "': #include " + includeline + "could not be found!") + + line = fs.readline() + + if line.find("#ifdef ") != -1 or line.find("#elif defined(") != -1: + if line.find("#ifdef ") != -1: + ifdefline = line.replace("#ifdef ", "").strip() + else: + ifdefline = line.replace("#elif defined(", "").strip() + ifdefline = ifdefline.replace(")", "").strip() + + if line.find("_EN_") != -1: + enumbase = ifdefline[:ifdefline.find("_EN_")] + ifdefline = ifdefline.replace("_EN_", "_") + line = line.replace("_EN_", "_") + if enumbase not in header_data.enums: + header_data.enums[enumbase] = [] + if ifdefline not in header_data.enums[enumbase]: + header_data.enums[enumbase].append(ifdefline) + + elif not ifdefline in header_data.conditionals: + header_data.conditionals += [ifdefline] + + if line.find("uniform") != -1 and line.lower().find("texunit:") != -1: + # texture unit + texunitstr = line[line.find(":") + 1:].strip() + if texunitstr == "auto": + texunit = "-1" + else: + texunit = str(int(texunitstr)) + uline = line[:line.lower().find("//")] + uline = uline.replace("uniform", "") + uline = uline.replace("highp", "") + uline = uline.replace(";", "") + lines = uline.split(",") + for x in lines: + + x = x.strip() + x = x[x.rfind(" ") + 1:] + if x.find("[") != -1: + # unfiorm array + x = x[:x.find("[")] + + if not x in header_data.texunit_names: + header_data.texunits += [(x, texunit)] + header_data.texunit_names += [x] + + elif line.find("uniform") != -1 and line.lower().find("ubo:") != -1: + # uniform buffer object + ubostr = line[line.find(":") + 1:].strip() + ubo = str(int(ubostr)) + uline = line[:line.lower().find("//")] + uline = uline[uline.find("uniform") + len("uniform"):] + uline = uline.replace("highp", "") + uline = uline.replace(";", "") + uline = uline.replace("{", "").strip() + lines = uline.split(",") + for x in lines: + + x = x.strip() + x = x[x.rfind(" ") + 1:] + if x.find("[") != -1: + # unfiorm array + x = x[:x.find("[")] + + if not x in header_data.ubo_names: + header_data.ubos += [(x, ubo)] + header_data.ubo_names += [x] + + elif line.find("uniform") != -1 and line.find("{") == -1 and line.find(";") != -1: + uline = line.replace("uniform", "") + uline = uline.replace(";", "") + lines = uline.split(",") + for x in lines: + + x = x.strip() + x = x[x.rfind(" ") + 1:] + if x.find("[") != -1: + # unfiorm array + x = x[:x.find("[")] + + if not x in header_data.uniforms: + header_data.uniforms += [x] + + if line.strip().find("attribute ") == 0 and line.find("attrib:") != -1: + uline = line.replace("in ", "") + uline = uline.replace("attribute ", "") + uline = uline.replace("highp ", "") + uline = uline.replace(";", "") + uline = uline[uline.find(" "):].strip() + + if uline.find("//") != -1: + name, bind = uline.split("//") + if bind.find("attrib:") != -1: + name = name.strip() + bind = bind.replace("attrib:", "").strip() + header_data.attributes += [(name, bind)] + + if line.strip().find("out ") == 0 and line.find("tfb:") != -1: + uline = line.replace("out ", "") + uline = uline.replace("highp ", "") + uline = uline.replace(";", "") + uline = uline[uline.find(" "):].strip() + + if uline.find("//") != -1: + name, bind = uline.split("//") + if bind.find("tfb:") != -1: + name = name.strip() + bind = bind.replace("tfb:", "").strip() + header_data.feedbacks += [(name, bind)] + + line = line.replace("\r", "") + line = line.replace("\n", "") + + if header_data.reading == "vertex": + header_data.vertex_lines += [line] + if header_data.reading == "fragment": + header_data.fragment_lines += [line] + + line = fs.readline() + header_data.line_offset += 1 + + fs.close() + + return header_data + + +def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2=False): + header_data = LegacyGLHeaderStruct() + include_file_in_legacygl_header(filename, header_data, 0) + + out_file = filename + ".gen.h" + fd = open(out_file, "w") + + enum_constants = [] + + fd.write("/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */\n") + + out_file_base = out_file + out_file_base = out_file_base[out_file_base.rfind("/") + 1:] + out_file_base = out_file_base[out_file_base.rfind("\\") + 1:] + out_file_ifdef = out_file_base.replace(".", "_").upper() + fd.write("#ifndef " + out_file_ifdef + class_suffix + "_120\n") + fd.write("#define " + out_file_ifdef + class_suffix + "_120\n") + + out_file_class = out_file_base.replace(".glsl.gen.h", "").title().replace("_", "").replace(".", "") + "Shader" + class_suffix + fd.write("\n\n") + fd.write("#include \"" + include + "\"\n\n\n") + fd.write("class " + out_file_class + " : public Shader" + class_suffix + " {\n\n") + fd.write("\t virtual String get_shader_name() const { return \"" + out_file_class + "\"; }\n") + + fd.write("public:\n\n") + + if header_data.conditionals: + fd.write("\tenum Conditionals {\n") + for x in header_data.conditionals: + fd.write("\t\t" + x.upper() + ",\n") + fd.write("\t};\n\n") + + if header_data.uniforms: + fd.write("\tenum Uniforms {\n") + for x in header_data.uniforms: + fd.write("\t\t" + x.upper() + ",\n") + fd.write("\t};\n\n") + + fd.write("\t_FORCE_INLINE_ int get_uniform(Uniforms p_uniform) const { return _get_uniform(p_uniform); }\n\n") + if header_data.conditionals: + fd.write("\t_FORCE_INLINE_ void set_conditional(Conditionals p_conditional,bool p_enable) { _set_conditional(p_conditional,p_enable); }\n\n") + fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; ERR_FAIL_COND( get_active()!=this );\n\n ") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, double p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint8_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int8_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint16_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int16_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Color& p_color) { _FU GLfloat col[4]={p_color.r,p_color.g,p_color.b,p_color.a}; glUniform4fv(get_uniform(p_uniform),1,col); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector2& p_vec2) { _FU GLfloat vec2[2]={p_vec2.x,p_vec2.y}; glUniform2fv(get_uniform(p_uniform),1,vec2); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Size2i& p_vec2) { _FU GLint vec2[2]={p_vec2.x,p_vec2.y}; glUniform2iv(get_uniform(p_uniform),1,vec2); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector3& p_vec3) { _FU GLfloat vec3[3]={p_vec3.x,p_vec3.y,p_vec3.z}; glUniform3fv(get_uniform(p_uniform),1,vec3); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b) { _FU glUniform2f(get_uniform(p_uniform),p_a,p_b); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c) { _FU glUniform3f(get_uniform(p_uniform),p_a,p_b,p_c); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c, float p_d) { _FU glUniform4f(get_uniform(p_uniform),p_a,p_b,p_c,p_d); }\n\n") + + fd.write("""\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Transform& p_transform) { _FU + + const Transform &tr = p_transform; + + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.basis.elements[0][0], + tr.basis.elements[1][0], + tr.basis.elements[2][0], + 0, + tr.basis.elements[0][1], + tr.basis.elements[1][1], + tr.basis.elements[2][1], + 0, + tr.basis.elements[0][2], + tr.basis.elements[1][2], + tr.basis.elements[2][2], + 0, + tr.origin.x, + tr.origin.y, + tr.origin.z, + 1 + }; + + + glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); + + + } + + """) + + fd.write("""_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Transform2D& p_transform) { _FU + + const Transform2D &tr = p_transform; + + GLfloat matrix[16]={ /* build a 16x16 matrix */ + tr.elements[0][0], + tr.elements[0][1], + 0, + 0, + tr.elements[1][0], + tr.elements[1][1], + 0, + 0, + 0, + 0, + 1, + 0, + tr.elements[2][0], + tr.elements[2][1], + 0, + 1 + }; + + + glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); + + + } + + """) + + fd.write("""_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const CameraMatrix& p_matrix) { _FU + + GLfloat matrix[16]; + + for (int i=0;i<4;i++) { + for (int j=0;j<4;j++) { + + matrix[i*4+j]=p_matrix.matrix[i][j]; + } + } + + glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); +}""") + + fd.write("\n\n#undef _FU\n\n\n") + + fd.write("\tvirtual void init() {\n\n") + + enum_value_count = 0 + + if header_data.enums: + + fd.write("\t\t//Written using math, given nonstandarity of 64 bits integer constants..\n") + fd.write("\t\tstatic const Enum _enums[]={\n") + + bitofs = len(header_data.conditionals) + enum_vals = [] + + for xv in header_data.enums: + x = header_data.enums[xv] + bits = 1 + amt = len(x) + while (2 ** bits < amt): + bits += 1 + strs = "{" + for i in range(amt): + strs += "\"#define " + x[i] + "\\n\"," + + c = {} + c["set_mask"] = "uint64_t(" + str(i) + ")<<" + str(bitofs) + c["clear_mask"] = "((uint64_t(1)<<40)-1) ^ (((uint64_t(1)<<" + str(bits) + ") - 1)<<" + str(bitofs) + ")" + enum_vals.append(c) + enum_constants.append(x[i]) + + strs += "NULL}" + + fd.write("\t\t\t{(uint64_t(1<<" + str(bits) + ")-1)<<" + str(bitofs) + "," + str(bitofs) + "," + strs + "},\n") + bitofs += bits + + fd.write("\t\t};\n\n") + + fd.write("\t\tstatic const EnumValue _enum_values[]={\n") + + enum_value_count = len(enum_vals) + for x in enum_vals: + fd.write("\t\t\t{" + x["set_mask"] + "," + x["clear_mask"] + "},\n") + + fd.write("\t\t};\n\n") + + conditionals_found = [] + if header_data.conditionals: + + fd.write("\t\tstatic const char* _conditional_strings[]={\n") + if header_data.conditionals: + for x in header_data.conditionals: + fd.write("\t\t\t\"#define " + x + "\\n\",\n") + conditionals_found.append(x) + fd.write("\t\t};\n\n") + else: + fd.write("\t\tstatic const char **_conditional_strings=NULL;\n") + + if header_data.uniforms: + + fd.write("\t\tstatic const char* _uniform_strings[]={\n") + if header_data.uniforms: + for x in header_data.uniforms: + fd.write("\t\t\t\"" + x + "\",\n") + fd.write("\t\t};\n\n") + else: + fd.write("\t\tstatic const char **_uniform_strings=NULL;\n") + + if output_attribs: + if header_data.attributes: + + fd.write("\t\tstatic AttributePair _attribute_pairs[]={\n") + for x in header_data.attributes: + fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") + fd.write("\t\t};\n\n") + else: + fd.write("\t\tstatic AttributePair *_attribute_pairs=NULL;\n") + + feedback_count = 0 + + if not gles2 and len(header_data.feedbacks): + + fd.write("\t\tstatic const Feedback _feedbacks[]={\n") + for x in header_data.feedbacks: + name = x[0] + cond = x[1] + if cond in conditionals_found: + fd.write("\t\t\t{\"" + name + "\"," + str(conditionals_found.index(cond)) + "},\n") + else: + fd.write("\t\t\t{\"" + name + "\",-1},\n") + + feedback_count += 1 + + fd.write("\t\t};\n\n") + else: + if gles2: + pass + else: + fd.write("\t\tstatic const Feedback* _feedbacks=NULL;\n") + + if header_data.texunits: + fd.write("\t\tstatic TexUnitPair _texunit_pairs[]={\n") + for x in header_data.texunits: + fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") + fd.write("\t\t};\n\n") + else: + fd.write("\t\tstatic TexUnitPair *_texunit_pairs=NULL;\n") + + if not gles2 and header_data.ubos: + fd.write("\t\tstatic UBOPair _ubo_pairs[]={\n") + for x in header_data.ubos: + fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") + fd.write("\t\t};\n\n") + else: + if gles2: + pass + else: + fd.write("\t\tstatic UBOPair *_ubo_pairs=NULL;\n") + + fd.write("\t\tstatic const char _vertex_code[]={\n") + for x in header_data.vertex_lines: + for c in x: + fd.write(str(ord(c)) + ",") + + fd.write(str(ord('\n')) + ",") + fd.write("\t\t0};\n\n") + + fd.write("\t\tstatic const int _vertex_code_start=" + str(header_data.vertex_offset) + ";\n") + + fd.write("\t\tstatic const char _fragment_code[]={\n") + for x in header_data.fragment_lines: + for c in x: + fd.write(str(ord(c)) + ",") + + fd.write(str(ord('\n')) + ",") + fd.write("\t\t0};\n\n") + + fd.write("\t\tstatic const int _fragment_code_start=" + str(header_data.fragment_offset) + ";\n") + + if output_attribs: + if gles2: + fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_attribute_pairs," + str( + len(header_data.attributes)) + ", _texunit_pairs," + str(len(header_data.texunits)) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") + else: + fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_attribute_pairs," + str( + len(header_data.attributes)) + ", _texunit_pairs," + str(len(header_data.texunits)) + ",_ubo_pairs," + str(len(header_data.ubos)) + ",_feedbacks," + str( + feedback_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") + else: + if gles2: + fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_texunit_pairs," + str( + len(header_data.texunits)) + ",_enums," + str(len(header_data.enums)) + ",_enum_values," + str( + enum_value_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") + else: + fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_texunit_pairs," + str( + len(header_data.texunits)) + ",_enums," + str(len(header_data.enums)) + ",_enum_values," + str(enum_value_count) + ",_ubo_pairs," + str(len(header_data.ubos)) + ",_feedbacks," + str( + feedback_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") + + fd.write("\t}\n\n") + + if enum_constants: + + fd.write("\tenum EnumConditionals {\n") + for x in enum_constants: + fd.write("\t\t" + x.upper() + ",\n") + fd.write("\t};\n\n") + fd.write("\tvoid set_enum_conditional(EnumConditionals p_cond) { _set_enum_conditional(p_cond); }\n") + + fd.write("};\n\n") + fd.write("#endif\n\n") + fd.close() + + +def build_gles3_headers(target, source, env): + for x in source: + build_legacygl_header(str(x), include="drivers/gles3/shader_gles3.h", class_suffix="GLES3", output_attribs=True) + + +def build_gles2_headers(target, source, env): + for x in source: + build_legacygl_header(str(x), include="drivers/gles2/shader_gles2.h", class_suffix="GLES2", output_attribs=True, gles2=True) + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/main/SCsub b/main/SCsub index 0692175799..9af102600e 100644 --- a/main/SCsub +++ b/main/SCsub @@ -1,128 +1,8 @@ #!/usr/bin/env python Import('env') -from compat import byte_to_str -from collections import OrderedDict - -def make_splash(target, source, env): - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - - with open(src, "rb") as f: - buf = f.read() - - with open(dst, "w") as g: - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef BOOT_SPLASH_H\n") - g.write("#define BOOT_SPLASH_H\n") - g.write('static const Color boot_splash_bg_color = Color::html("#232323");\n') - g.write("static const unsigned char boot_splash_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") - - -def make_splash_editor(target, source, env): - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - - with open(src, "rb") as f: - buf = f.read() - - with open(dst, "w") as g: - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef BOOT_SPLASH_EDITOR_H\n") - g.write("#define BOOT_SPLASH_EDITOR_H\n") - g.write('static const Color boot_splash_editor_bg_color = Color::html("#232323");\n') - g.write("static const unsigned char boot_splash_editor_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") - - -def make_app_icon(target, source, env): - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - - with open(src, "rb") as f: - buf = f.read() - - with open(dst, "w") as g: - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef APP_ICON_H\n") - g.write("#define APP_ICON_H\n") - g.write("static const unsigned char app_icon_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") - -def make_default_controller_mappings(target, source, env): - dst = target[0].srcnode().abspath - g = open(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#include \"default_controller_mappings.h\"\n") - g.write("#include \"typedefs.h\"\n") - - # ensure mappings have a consistent order - platform_mappings = OrderedDict() - for src in source: - src_path = src.srcnode().abspath - with open(src_path, "r") as f: - # read mapping file and skip header - mapping_file_lines = f.readlines()[2:] - - current_platform = None - for line in mapping_file_lines: - if not line: - continue - line = line.strip() - if len(line) == 0: - continue - if line[0] == "#": - current_platform = line[1:].strip() - if current_platform not in platform_mappings: - platform_mappings[current_platform] = {} - elif current_platform: - line_parts = line.split(",") - guid = line_parts[0] - if guid in platform_mappings[current_platform]: - g.write("// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(src_path, current_platform, platform_mappings[current_platform][guid])) - valid_mapping = True - for input_map in line_parts[2:]: - if "+" in input_map or "-" in input_map or "~" in input_map: - g.write("// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format(src_path, current_platform, line)) - valid_mapping = False - break - if valid_mapping: - platform_mappings[current_platform][guid] = line - - platform_variables = { - "Linux": "#if X11_ENABLED", - "Windows": "#ifdef WINDOWS_ENABLED", - "Mac OS X": "#ifdef OSX_ENABLED", - "Android": "#if defined(__ANDROID__)", - "iOS": "#ifdef IPHONE_ENABLED", - "Javascript": "#ifdef JAVASCRIPT_ENABLED", - "UWP": "#ifdef UWP_ENABLED", - } - - g.write("const char* DefaultControllerMappings::mappings[] = {\n") - for platform, mappings in platform_mappings.items(): - variable = platform_variables[platform] - g.write("{}\n".format(variable)) - for mapping in mappings.values(): - g.write("\t\"{}\",\n".format(mapping)) - g.write("#endif\n") - - g.write("\tNULL\n};\n") - g.close() +from platform_methods import run_in_subprocess +import main_builders env.main_sources = [] env.add_source_files(env.main_sources, "*.cpp") @@ -131,20 +11,20 @@ env.add_source_files(env.main_sources, "*.cpp") controller_databases = ["#main/gamecontrollerdb.txt", "#main/gamecontrollerdb_205.txt", "#main/gamecontrollerdb_204.txt", "#main/godotcontrollerdb.txt"] env.Depends("#main/default_controller_mappings.gen.cpp", controller_databases) -env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_databases, make_default_controller_mappings) +env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_databases, run_in_subprocess(main_builders.make_default_controller_mappings)) env.main_sources.append("#main/default_controller_mappings.gen.cpp") Export('env') env.Depends("#main/splash.gen.h", "#main/splash.png") -env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", make_splash) +env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash)) env.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png") -env.CommandNoCache("#main/splash_editor.gen.h", "#main/splash_editor.png", make_splash_editor) +env.CommandNoCache("#main/splash_editor.gen.h", "#main/splash_editor.png", run_in_subprocess(main_builders.make_splash_editor)) env.Depends("#main/app_icon.gen.h", "#main/app_icon.png") -env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", make_app_icon) +env.CommandNoCache("#main/app_icon.gen.h", "#main/app_icon.png", run_in_subprocess(main_builders.make_app_icon)) SConscript('tests/SCsub') diff --git a/main/input_default.cpp b/main/input_default.cpp index 29d30110e3..4363fc1c88 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -684,7 +684,7 @@ void InputDefault::joy_button(int p_device, int p_button, bool p_pressed) { return; }; - Map<int, JoyEvent>::Element *el = map_db[joy.mapping].buttons.find(p_button); + const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].buttons.find(p_button); if (!el) { //don't process un-mapped events for now, it could mess things up badly for devices with additional buttons/axis //return _button_event(p_last_id, p_device, p_button, p_pressed); @@ -755,7 +755,7 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { return; }; - Map<int, JoyEvent>::Element *el = map_db[joy.mapping].axis.find(p_axis); + const Map<int, JoyEvent>::Element *el = map_db[joy.mapping].axis.find(p_axis); if (!el) { //return _axis_event(p_last_id, p_device, p_axis, p_value); return; @@ -831,7 +831,7 @@ void InputDefault::joy_hat(int p_device, int p_val) { _THREAD_SAFE_METHOD_; const Joypad &joy = joy_names[p_device]; - JoyEvent *map; + const JoyEvent *map; if (joy.mapping == -1) { map = hat_map_default; diff --git a/main/main.cpp b/main/main.cpp index 56dd5f73e7..2fd9cfa734 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -265,7 +265,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" -s, --script <script> Run a script.\n"); OS::get_singleton()->print(" --check-only Only parse for errors and quit (use with --script).\n"); #ifdef TOOLS_ENABLED - OS::get_singleton()->print(" --export <target> Export the project using the given export target. Export only main pack if path ends with .pck or .zip'.\n"); + OS::get_singleton()->print(" --export <target> Export the project using the given export target. Export only main pack if path ends with .pck or .zip.\n"); OS::get_singleton()->print(" --export-debug <target> Like --export, but use debug template.\n"); OS::get_singleton()->print(" --doctool <path> Dump the engine API reference to the given <path> in XML format, merging if existing files are found.\n"); OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n"); @@ -1257,14 +1257,14 @@ bool Main::start() { #endif } else if (args[i].length() && args[i][0] != '-' && game_path == "") { game_path = args[i]; + } else if (args[i] == "--check-only") { + check_only = true; } //parameters that have an argument to the right else if (i < (args.size() - 1)) { bool parsed_pair = true; if (args[i] == "-s" || args[i] == "--script") { script = args[i + 1]; - } else if (args[i] == "--check-only") { - check_only = true; } else if (args[i] == "--test") { test = args[i + 1]; #ifdef TOOLS_ENABLED @@ -1734,8 +1734,11 @@ bool Main::iteration() { int physics_fps = Engine::get_singleton()->get_iterations_per_second(); float frame_slice = 1.0 / physics_fps; + float time_scale = Engine::get_singleton()->get_time_scale(); + MainFrameTime advance = main_timer_sync.advance(frame_slice, physics_fps); double step = advance.idle_step; + double scaled_step = step * time_scale; Engine::get_singleton()->_frame_step = step; @@ -1757,8 +1760,6 @@ bool Main::iteration() { advance.physics_steps = max_physics_steps; } - float time_scale = Engine::get_singleton()->get_time_scale(); - bool exit = false; Engine::get_singleton()->_in_physics = true; @@ -1805,11 +1806,11 @@ bool Main::iteration() { if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) { if (VisualServer::get_singleton()->has_changed()) { - VisualServer::get_singleton()->draw(); // flush visual commands + VisualServer::get_singleton()->draw(true, scaled_step); // flush visual commands Engine::get_singleton()->frames_drawn++; } } else { - VisualServer::get_singleton()->draw(); // flush visual commands + VisualServer::get_singleton()->draw(true, scaled_step); // flush visual commands Engine::get_singleton()->frames_drawn++; force_redraw_requested = false; } diff --git a/main/main_builders.py b/main/main_builders.py new file mode 100644 index 0000000000..6d45768493 --- /dev/null +++ b/main/main_builders.py @@ -0,0 +1,130 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main +from compat import byte_to_str +from collections import OrderedDict + + +def make_splash(target, source, env): + src = source[0] + dst = target[0] + + with open(src, "rb") as f: + buf = f.read() + + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef BOOT_SPLASH_H\n") + g.write("#define BOOT_SPLASH_H\n") + g.write('static const Color boot_splash_bg_color = Color::html("#232323");\n') + g.write("static const unsigned char boot_splash_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + +def make_splash_editor(target, source, env): + src = source[0] + dst = target[0] + + with open(src, "rb") as f: + buf = f.read() + + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef BOOT_SPLASH_EDITOR_H\n") + g.write("#define BOOT_SPLASH_EDITOR_H\n") + g.write('static const Color boot_splash_editor_bg_color = Color::html("#232323");\n') + g.write("static const unsigned char boot_splash_editor_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + +def make_app_icon(target, source, env): + src = source[0] + dst = target[0] + + with open(src, "rb") as f: + buf = f.read() + + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef APP_ICON_H\n") + g.write("#define APP_ICON_H\n") + g.write("static const unsigned char app_icon_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + +def make_default_controller_mappings(target, source, env): + dst = target[0] + g = open(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#include \"default_controller_mappings.h\"\n") + g.write("#include \"typedefs.h\"\n") + + # ensure mappings have a consistent order + platform_mappings = OrderedDict() + for src_path in source: + with open(src_path, "r") as f: + # read mapping file and skip header + mapping_file_lines = f.readlines()[2:] + + current_platform = None + for line in mapping_file_lines: + if not line: + continue + line = line.strip() + if len(line) == 0: + continue + if line[0] == "#": + current_platform = line[1:].strip() + if current_platform not in platform_mappings: + platform_mappings[current_platform] = {} + elif current_platform: + line_parts = line.split(",") + guid = line_parts[0] + if guid in platform_mappings[current_platform]: + g.write("// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(src_path, current_platform, platform_mappings[current_platform][guid])) + valid_mapping = True + for input_map in line_parts[2:]: + if "+" in input_map or "-" in input_map or "~" in input_map: + g.write("// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format(src_path, current_platform, line)) + valid_mapping = False + break + if valid_mapping: + platform_mappings[current_platform][guid] = line + + platform_variables = { + "Linux": "#if X11_ENABLED", + "Windows": "#ifdef WINDOWS_ENABLED", + "Mac OS X": "#ifdef OSX_ENABLED", + "Android": "#if defined(__ANDROID__)", + "iOS": "#ifdef IPHONE_ENABLED", + "Javascript": "#ifdef JAVASCRIPT_ENABLED", + "UWP": "#ifdef UWP_ENABLED", + } + + g.write("const char* DefaultControllerMappings::mappings[] = {\n") + for platform, mappings in platform_mappings.items(): + variable = platform_variables[platform] + g.write("{}\n".format(variable)) + for mapping in mappings.values(): + g.write("\t\"{}\",\n".format(mapping)) + g.write("#endif\n") + + g.write("\tNULL\n};\n") + g.close() + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 5f3a2c1dc8..0a9d03c1b7 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -924,8 +924,8 @@ MainLoop *test(TestType p_type) { Vector<uint8_t> buf; int flen = fa->get_len(); buf.resize(fa->get_len() + 1); - fa->get_buffer(&buf[0], flen); - buf[flen] = 0; + fa->get_buffer(buf.ptrw(), flen); + buf.write[flen] = 0; String code; code.parse_utf8((const char *)&buf[0]); diff --git a/main/tests/test_io.cpp b/main/tests/test_io.cpp index 08dc374ed1..4f98955995 100644 --- a/main/tests/test_io.cpp +++ b/main/tests/test_io.cpp @@ -103,7 +103,7 @@ MainLoop *test() { int len = z->get_len(); Vector<uint8_t> zip; zip.resize(len); - z->get_buffer(&zip[0], len); + z->get_buffer(zip.ptrw(), len); z->close(); memdelete(z); diff --git a/main/tests/test_math.cpp b/main/tests/test_math.cpp index 8b71c5dc70..1a72416d6a 100644 --- a/main/tests/test_math.cpp +++ b/main/tests/test_math.cpp @@ -503,8 +503,8 @@ MainLoop *test() { Vector<uint8_t> buf; int flen = fa->get_len(); buf.resize(fa->get_len() + 1); - fa->get_buffer(&buf[0], flen); - buf[flen] = 0; + fa->get_buffer(buf.ptrw(), flen); + buf.write[flen] = 0; String code; code.parse_utf8((const char *)&buf[0]); diff --git a/main/tests/test_physics.cpp b/main/tests/test_physics.cpp index 475663dabe..99c8fce70e 100644 --- a/main/tests/test_physics.cpp +++ b/main/tests/test_physics.cpp @@ -228,11 +228,11 @@ protected: for (int i = 0; i < p_width; i++) { - grid[i].resize(p_height); + grid.write[i].resize(p_height); for (int j = 0; j < p_height; j++) { - grid[i][j] = 1.0 + Math::random(-p_cellheight, p_cellheight); + grid.write[i].write[j] = 1.0 + Math::random(-p_cellheight, p_cellheight); } } diff --git a/methods.py b/methods.py index 33d503a41a..e9450d95e2 100644 --- a/methods.py +++ b/methods.py @@ -1,772 +1,52 @@ import os -from compat import iteritems, itervalues, open_utf8, escape_string +import os.path +import sys +import re +import glob +import string +import datetime +import subprocess +from compat import iteritems, isbasestring def add_source_files(self, sources, filetype, lib_env=None, shared=False): - import glob - import string - # if not lib_objects: - if not lib_env: - lib_env = self - if type(filetype) == type(""): - - dir = self.Dir('.').abspath - list = glob.glob(dir + "/" + filetype) - for f in list: - sources.append(self.Object(f)) - else: - for f in filetype: - sources.append(self.Object(f)) - - - -class LegacyGLHeaderStruct: - - def __init__(self): - self.vertex_lines = [] - self.fragment_lines = [] - self.uniforms = [] - self.attributes = [] - self.feedbacks = [] - self.fbos = [] - self.conditionals = [] - self.enums = {} - self.texunits = [] - self.texunit_names = [] - self.ubos = [] - self.ubo_names = [] - - self.vertex_included_files = [] - self.fragment_included_files = [] - - self.reading = "" - self.line_offset = 0 - self.vertex_offset = 0 - self.fragment_offset = 0 - - -def include_file_in_legacygl_header(filename, header_data, depth): - fs = open(filename, "r") - line = fs.readline() - - while(line): - - if (line.find("[vertex]") != -1): - header_data.reading = "vertex" - line = fs.readline() - header_data.line_offset += 1 - header_data.vertex_offset = header_data.line_offset - continue - - if (line.find("[fragment]") != -1): - header_data.reading = "fragment" - line = fs.readline() - header_data.line_offset += 1 - header_data.fragment_offset = header_data.line_offset - continue - - while(line.find("#include ") != -1): - includeline = line.replace("#include ", "").strip()[1:-1] - - import os.path - - included_file = os.path.relpath(os.path.dirname(filename) + "/" + includeline) - if (not included_file in header_data.vertex_included_files and header_data.reading == "vertex"): - header_data.vertex_included_files += [included_file] - if(include_file_in_legacygl_header(included_file, header_data, depth + 1) == None): - print("Error in file '" + filename + "': #include " + includeline + "could not be found!") - elif (not included_file in header_data.fragment_included_files and header_data.reading == "fragment"): - header_data.fragment_included_files += [included_file] - if(include_file_in_legacygl_header(included_file, header_data, depth + 1) == None): - print("Error in file '" + filename + "': #include " + includeline + "could not be found!") - - line = fs.readline() - - if (line.find("#ifdef ") != -1 or line.find("#elif defined(") != -1): - if (line.find("#ifdef ") != -1): - ifdefline = line.replace("#ifdef ", "").strip() - else: - ifdefline = line.replace("#elif defined(", "").strip() - ifdefline = ifdefline.replace(")", "").strip() - - if (line.find("_EN_") != -1): - enumbase = ifdefline[:ifdefline.find("_EN_")] - ifdefline = ifdefline.replace("_EN_", "_") - line = line.replace("_EN_", "_") - if (enumbase not in header_data.enums): - header_data.enums[enumbase] = [] - if (ifdefline not in header_data.enums[enumbase]): - header_data.enums[enumbase].append(ifdefline) - - elif (not ifdefline in header_data.conditionals): - header_data.conditionals += [ifdefline] - - if (line.find("uniform") != -1 and line.lower().find("texunit:") != -1): - # texture unit - texunitstr = line[line.find(":") + 1:].strip() - if (texunitstr == "auto"): - texunit = "-1" - else: - texunit = str(int(texunitstr)) - uline = line[:line.lower().find("//")] - uline = uline.replace("uniform", "") - uline = uline.replace("highp", "") - uline = uline.replace(";", "") - lines = uline.split(",") - for x in lines: - - x = x.strip() - x = x[x.rfind(" ") + 1:] - if (x.find("[") != -1): - # unfiorm array - x = x[:x.find("[")] - - if (not x in header_data.texunit_names): - header_data.texunits += [(x, texunit)] - header_data.texunit_names += [x] - - elif (line.find("uniform") != -1 and line.lower().find("ubo:") != -1): - # uniform buffer object - ubostr = line[line.find(":") + 1:].strip() - ubo = str(int(ubostr)) - uline = line[:line.lower().find("//")] - uline = uline[uline.find("uniform") + len("uniform"):] - uline = uline.replace("highp", "") - uline = uline.replace(";", "") - uline = uline.replace("{", "").strip() - lines = uline.split(",") - for x in lines: - - x = x.strip() - x = x[x.rfind(" ") + 1:] - if (x.find("[") != -1): - # unfiorm array - x = x[:x.find("[")] - - if (not x in header_data.ubo_names): - header_data.ubos += [(x, ubo)] - header_data.ubo_names += [x] - - elif (line.find("uniform") != -1 and line.find("{") == -1 and line.find(";") != -1): - uline = line.replace("uniform", "") - uline = uline.replace(";", "") - lines = uline.split(",") - for x in lines: - - x = x.strip() - x = x[x.rfind(" ") + 1:] - if (x.find("[") != -1): - # unfiorm array - x = x[:x.find("[")] - - if (not x in header_data.uniforms): - header_data.uniforms += [x] - - if (line.strip().find("attribute ") == 0 and line.find("attrib:") != -1): - uline = line.replace("in ", "") - uline = uline.replace("attribute ", "") - uline = uline.replace("highp ", "") - uline = uline.replace(";", "") - uline = uline[uline.find(" "):].strip() - - if (uline.find("//") != -1): - name, bind = uline.split("//") - if (bind.find("attrib:") != -1): - name = name.strip() - bind = bind.replace("attrib:", "").strip() - header_data.attributes += [(name, bind)] - - if (line.strip().find("out ") == 0 and line.find("tfb:") != -1): - uline = line.replace("out ", "") - uline = uline.replace("highp ", "") - uline = uline.replace(";", "") - uline = uline[uline.find(" "):].strip() - - if (uline.find("//") != -1): - name, bind = uline.split("//") - if (bind.find("tfb:") != -1): - name = name.strip() - bind = bind.replace("tfb:", "").strip() - header_data.feedbacks += [(name, bind)] - - line = line.replace("\r", "") - line = line.replace("\n", "") - - if (header_data.reading == "vertex"): - header_data.vertex_lines += [line] - if (header_data.reading == "fragment"): - header_data.fragment_lines += [line] - - line = fs.readline() - header_data.line_offset += 1 - - fs.close() - - return header_data - - -def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2=False): - - header_data = LegacyGLHeaderStruct() - include_file_in_legacygl_header(filename, header_data, 0) - - out_file = filename + ".gen.h" - fd = open(out_file, "w") - - enum_constants = [] - - fd.write("/* WARNING, THIS FILE WAS GENERATED, DO NOT EDIT */\n") - - out_file_base = out_file - out_file_base = out_file_base[out_file_base.rfind("/") + 1:] - out_file_base = out_file_base[out_file_base.rfind("\\") + 1:] - out_file_ifdef = out_file_base.replace(".", "_").upper() - fd.write("#ifndef " + out_file_ifdef + class_suffix + "_120\n") - fd.write("#define " + out_file_ifdef + class_suffix + "_120\n") - - out_file_class = out_file_base.replace(".glsl.gen.h", "").title().replace("_", "").replace(".", "") + "Shader" + class_suffix - fd.write("\n\n") - fd.write("#include \"" + include + "\"\n\n\n") - fd.write("class " + out_file_class + " : public Shader" + class_suffix + " {\n\n") - fd.write("\t virtual String get_shader_name() const { return \"" + out_file_class + "\"; }\n") - - fd.write("public:\n\n") - - if (len(header_data.conditionals)): - fd.write("\tenum Conditionals {\n") - for x in header_data.conditionals: - fd.write("\t\t" + x.upper() + ",\n") - fd.write("\t};\n\n") - - if (len(header_data.uniforms)): - fd.write("\tenum Uniforms {\n") - for x in header_data.uniforms: - fd.write("\t\t" + x.upper() + ",\n") - fd.write("\t};\n\n") - - fd.write("\t_FORCE_INLINE_ int get_uniform(Uniforms p_uniform) const { return _get_uniform(p_uniform); }\n\n") - if (len(header_data.conditionals)): - - fd.write("\t_FORCE_INLINE_ void set_conditional(Conditionals p_conditional,bool p_enable) { _set_conditional(p_conditional,p_enable); }\n\n") - fd.write("\t#define _FU if (get_uniform(p_uniform)<0) return; ERR_FAIL_COND( get_active()!=this );\n\n ") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, double p_value) { _FU glUniform1f(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint8_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int8_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint16_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int16_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Color& p_color) { _FU GLfloat col[4]={p_color.r,p_color.g,p_color.b,p_color.a}; glUniform4fv(get_uniform(p_uniform),1,col); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector2& p_vec2) { _FU GLfloat vec2[2]={p_vec2.x,p_vec2.y}; glUniform2fv(get_uniform(p_uniform),1,vec2); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector3& p_vec3) { _FU GLfloat vec3[3]={p_vec3.x,p_vec3.y,p_vec3.z}; glUniform3fv(get_uniform(p_uniform),1,vec3); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b) { _FU glUniform2f(get_uniform(p_uniform),p_a,p_b); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c) { _FU glUniform3f(get_uniform(p_uniform),p_a,p_b,p_c); }\n\n") - fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c, float p_d) { _FU glUniform4f(get_uniform(p_uniform),p_a,p_b,p_c,p_d); }\n\n") - - fd.write("""\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Transform& p_transform) { _FU - - const Transform &tr = p_transform; - - GLfloat matrix[16]={ /* build a 16x16 matrix */ - tr.basis.elements[0][0], - tr.basis.elements[1][0], - tr.basis.elements[2][0], - 0, - tr.basis.elements[0][1], - tr.basis.elements[1][1], - tr.basis.elements[2][1], - 0, - tr.basis.elements[0][2], - tr.basis.elements[1][2], - tr.basis.elements[2][2], - 0, - tr.origin.x, - tr.origin.y, - tr.origin.z, - 1 - }; - - - glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); - - - } - - """) - - fd.write("""_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Transform2D& p_transform) { _FU - - const Transform2D &tr = p_transform; - - GLfloat matrix[16]={ /* build a 16x16 matrix */ - tr.elements[0][0], - tr.elements[0][1], - 0, - 0, - tr.elements[1][0], - tr.elements[1][1], - 0, - 0, - 0, - 0, - 1, - 0, - tr.elements[2][0], - tr.elements[2][1], - 0, - 1 - }; - - - glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); - - - } - - """) - - fd.write("""_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const CameraMatrix& p_matrix) { _FU - - GLfloat matrix[16]; - - for (int i=0;i<4;i++) { - for (int j=0;j<4;j++) { - - matrix[i*4+j]=p_matrix.matrix[i][j]; - } - } - - glUniformMatrix4fv(get_uniform(p_uniform),1,false,matrix); - } """) - - fd.write("\n\n#undef _FU\n\n\n") - - fd.write("\tvirtual void init() {\n\n") - - enum_value_count = 0 - - if (len(header_data.enums)): - - fd.write("\t\t//Written using math, given nonstandarity of 64 bits integer constants..\n") - fd.write("\t\tstatic const Enum _enums[]={\n") - - bitofs = len(header_data.conditionals) - enum_vals = [] - - for xv in header_data.enums: - x = header_data.enums[xv] - bits = 1 - amt = len(x) - while(2**bits < amt): - bits += 1 - strs = "{" - for i in range(amt): - strs += "\"#define " + x[i] + "\\n\"," - - v = {} - v["set_mask"] = "uint64_t(" + str(i) + ")<<" + str(bitofs) - v["clear_mask"] = "((uint64_t(1)<<40)-1) ^ (((uint64_t(1)<<" + str(bits) + ") - 1)<<" + str(bitofs) + ")" - enum_vals.append(v) - enum_constants.append(x[i]) - - strs += "NULL}" - - fd.write("\t\t\t{(uint64_t(1<<" + str(bits) + ")-1)<<" + str(bitofs) + "," + str(bitofs) + "," + strs + "},\n") - bitofs += bits - - fd.write("\t\t};\n\n") - - fd.write("\t\tstatic const EnumValue _enum_values[]={\n") - - enum_value_count = len(enum_vals) - for x in enum_vals: - fd.write("\t\t\t{" + x["set_mask"] + "," + x["clear_mask"] + "},\n") - - fd.write("\t\t};\n\n") - conditionals_found = [] - if (len(header_data.conditionals)): + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = glob.glob(dir_path + "/" + filetype) - fd.write("\t\tstatic const char* _conditional_strings[]={\n") - if (len(header_data.conditionals)): - for x in header_data.conditionals: - fd.write("\t\t\t\"#define " + x + "\\n\",\n") - conditionals_found.append(x) - fd.write("\t\t};\n\n") - else: - fd.write("\t\tstatic const char **_conditional_strings=NULL;\n") - - if (len(header_data.uniforms)): - - fd.write("\t\tstatic const char* _uniform_strings[]={\n") - if (len(header_data.uniforms)): - for x in header_data.uniforms: - fd.write("\t\t\t\"" + x + "\",\n") - fd.write("\t\t};\n\n") - else: - fd.write("\t\tstatic const char **_uniform_strings=NULL;\n") - - if output_attribs: - if (len(header_data.attributes)): - - fd.write("\t\tstatic AttributePair _attribute_pairs[]={\n") - for x in header_data.attributes: - fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") - fd.write("\t\t};\n\n") - else: - fd.write("\t\tstatic AttributePair *_attribute_pairs=NULL;\n") - - feedback_count = 0 - - if (not gles2 and len(header_data.feedbacks)): - - fd.write("\t\tstatic const Feedback _feedbacks[]={\n") - for x in header_data.feedbacks: - name = x[0] - cond = x[1] - if (cond in conditionals_found): - fd.write("\t\t\t{\"" + name + "\"," + str(conditionals_found.index(cond)) + "},\n") - else: - fd.write("\t\t\t{\"" + name + "\",-1},\n") - - feedback_count += 1 - - fd.write("\t\t};\n\n") - else: - if gles2: - pass - else: - fd.write("\t\tstatic const Feedback* _feedbacks=NULL;\n") - - if (len(header_data.texunits)): - fd.write("\t\tstatic TexUnitPair _texunit_pairs[]={\n") - for x in header_data.texunits: - fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") - fd.write("\t\t};\n\n") - else: - fd.write("\t\tstatic TexUnitPair *_texunit_pairs=NULL;\n") - - if (not gles2 and len(header_data.ubos)): - fd.write("\t\tstatic UBOPair _ubo_pairs[]={\n") - for x in header_data.ubos: - fd.write("\t\t\t{\"" + x[0] + "\"," + x[1] + "},\n") - fd.write("\t\t};\n\n") - else: - if gles2: - pass - else: - fd.write("\t\tstatic UBOPair *_ubo_pairs=NULL;\n") - - fd.write("\t\tstatic const char _vertex_code[]={\n") - for x in header_data.vertex_lines: - for i in range(len(x)): - fd.write(str(ord(x[i])) + ",") - - fd.write(str(ord('\n')) + ",") - fd.write("\t\t0};\n\n") - - fd.write("\t\tstatic const int _vertex_code_start=" + str(header_data.vertex_offset) + ";\n") - - fd.write("\t\tstatic const char _fragment_code[]={\n") - for x in header_data.fragment_lines: - for i in range(len(x)): - fd.write(str(ord(x[i])) + ",") - - fd.write(str(ord('\n')) + ",") - fd.write("\t\t0};\n\n") - - fd.write("\t\tstatic const int _fragment_code_start=" + str(header_data.fragment_offset) + ";\n") - - if output_attribs: - if gles2: - fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_attribute_pairs," + str(len(header_data.attributes)) + ", _texunit_pairs," + str(len(header_data.texunits)) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") - else: - fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_attribute_pairs," + str(len(header_data.attributes)) + ", _texunit_pairs," + str(len(header_data.texunits)) + ",_ubo_pairs," + str(len(header_data.ubos)) + ",_feedbacks," + str(feedback_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") - else: - if gles2: - fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_texunit_pairs," + str(len(header_data.texunits)) + ",_enums," + str(len(header_data.enums)) + ",_enum_values," + str(enum_value_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") - else: - fd.write("\t\tsetup(_conditional_strings," + str(len(header_data.conditionals)) + ",_uniform_strings," + str(len(header_data.uniforms)) + ",_texunit_pairs," + str(len(header_data.texunits)) + ",_enums," + str(len(header_data.enums)) + ",_enum_values," + str(enum_value_count) + ",_ubo_pairs," + str(len(header_data.ubos)) + ",_feedbacks," + str(feedback_count) + ",_vertex_code,_fragment_code,_vertex_code_start,_fragment_code_start);\n") - - fd.write("\t}\n\n") - - if (len(enum_constants)): + for path in filetype: + sources.append(self.Object(path)) - fd.write("\tenum EnumConditionals {\n") - for x in enum_constants: - fd.write("\t\t" + x.upper() + ",\n") - fd.write("\t};\n\n") - fd.write("\tvoid set_enum_conditional(EnumConditionals p_cond) { _set_enum_conditional(p_cond); }\n") - fd.write("};\n\n") - fd.write("#endif\n\n") - fd.close() - - -def build_gles3_headers(target, source, env): - - for x in source: - build_legacygl_header(str(x), include="drivers/gles3/shader_gles3.h", class_suffix="GLES3", output_attribs=True) - - -def build_gles2_headers(target, source, env): - - for x in source: - build_legacygl_header(str(x), include="drivers/gles2/shader_gles2.h", class_suffix="GLES2", output_attribs=True, gles2=True) - -def make_authors_header(target, source, env): - - sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] - sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"] - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - f = open_utf8(src, "r") - g = open_utf8(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _EDITOR_AUTHORS_H\n") - g.write("#define _EDITOR_AUTHORS_H\n") - - current_section = "" - reading = False - - def close_section(): - g.write("\t0\n") - g.write("};\n") - - for line in f: - if reading: - if line.startswith(" "): - g.write("\t\"" + escape_string(line.strip()) + "\",\n") - continue - if line.startswith("## "): - if reading: - close_section() - reading = False - for i in range(len(sections)): - if line.strip().endswith(sections[i]): - current_section = escape_string(sections_id[i]) - reading = True - g.write("const char *const " + current_section + "[] = {\n") - break - - if reading: - close_section() - - g.write("#endif\n") - - g.close() - f.close() - -def make_donors_header(target, source, env): - - sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", - "Gold donors", "Silver donors", "Bronze donors"] - sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI", - "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"] - - src = source[0].srcnode().abspath - dst = target[0].srcnode().abspath - f = open_utf8(src, "r") - g = open_utf8(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef _EDITOR_DONORS_H\n") - g.write("#define _EDITOR_DONORS_H\n") - - current_section = "" - reading = False - - def close_section(): - g.write("\t0\n") - g.write("};\n") - - for line in f: - if reading >= 0: - if line.startswith(" "): - g.write("\t\"" + escape_string(line.strip()) + "\",\n") - continue - if line.startswith("## "): - if reading: - close_section() - reading = False - for i in range(len(sections)): - if line.strip().endswith(sections[i]): - current_section = escape_string(sections_id[i]) - reading = True - g.write("const char *const " + current_section + "[] = {\n") - break - - if reading: - close_section() - - g.write("#endif\n") - - g.close() - f.close() - - -def make_license_header(target, source, env): - src_copyright = source[0].srcnode().abspath - src_license = source[1].srcnode().abspath - dst = target[0].srcnode().abspath - - class LicenseReader: - def __init__(self, license_file): - self._license_file = license_file - self.line_num = 0 - self.current = self.next_line() - - def next_line(self): - line = self._license_file.readline() - self.line_num += 1 - while line.startswith("#"): - line = self._license_file.readline() - self.line_num += 1 - self.current = line - return line - - def next_tag(self): - if not ':' in self.current: - return ('',[]) - tag, line = self.current.split(":", 1) - lines = [line.strip()] - while self.next_line() and self.current.startswith(" "): - lines.append(self.current.strip()) - return (tag, lines) - - from collections import OrderedDict - projects = OrderedDict() - license_list = [] - - with open_utf8(src_copyright, "r") as copyright_file: - reader = LicenseReader(copyright_file) - part = {} - while reader.current: - tag, content = reader.next_tag() - if tag in ("Files", "Copyright", "License"): - part[tag] = content[:] - elif tag == "Comment": - # attach part to named project - projects[content[0]] = projects.get(content[0], []) + [part] - - if not tag or not reader.current: - # end of a paragraph start a new part - if "License" in part and not "Files" in part: - # no Files tag in this one, so assume standalone license - license_list.append(part["License"]) - part = {} - reader.next_line() - - data_list = [] - for project in itervalues(projects): - for part in project: - part["file_index"] = len(data_list) - data_list += part["Files"] - part["copyright_index"] = len(data_list) - data_list += part["Copyright"] - - with open_utf8(dst, "w") as f: - - f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - f.write("#ifndef _EDITOR_LICENSE_H\n") - f.write("#define _EDITOR_LICENSE_H\n") - f.write("const char *const GODOT_LICENSE_TEXT =") - - with open_utf8(src_license, "r") as license_file: - for line in license_file: - escaped_string = escape_string(line.strip()) - f.write("\n\t\t\"" + escaped_string + "\\n\"") - f.write(";\n\n") - - f.write("struct ComponentCopyrightPart {\n" - "\tconst char *license;\n" - "\tconst char *const *files;\n" - "\tconst char *const *copyright_statements;\n" - "\tint file_count;\n" - "\tint copyright_count;\n" - "};\n\n") - - f.write("struct ComponentCopyright {\n" - "\tconst char *name;\n" - "\tconst ComponentCopyrightPart *parts;\n" - "\tint part_count;\n" - "};\n\n") - - f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") - for line in data_list: - f.write("\t\"" + escape_string(line) + "\",\n") - f.write("};\n\n") - - f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") - part_index = 0 - part_indexes = {} - for project_name, project in iteritems(projects): - part_indexes[project_name] = part_index - for part in project: - f.write("\t{ \"" + escape_string(part["License"][0]) + "\", " - + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " - + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " - + str(len(part["Files"])) + ", " - + str(len(part["Copyright"])) + " },\n") - part_index += 1 - f.write("};\n\n") - - f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") - - f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") - for project_name, project in iteritems(projects): - f.write("\t{ \"" + escape_string(project_name) + "\", " - + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " - + str(len(project)) + " },\n") - f.write("};\n\n") - - f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") - - f.write("const char *const LICENSE_NAMES[] = {\n") - for l in license_list: - f.write("\t\"" + escape_string(l[0]) + "\",\n") - f.write("};\n\n") - - f.write("const char *const LICENSE_BODIES[] = {\n\n") - for l in license_list: - for line in l[1:]: - if line == ".": - f.write("\t\"\\n\"\n") - else: - f.write("\t\"" + escape_string(line) + "\\n\"\n") - f.write("\t\"\",\n\n") - f.write("};\n\n") - - f.write("#endif\n") def add_module_version_string(self,s): self.module_version_string += "." + s + def update_version(module_version_string=""): build_name = "custom_build" - if (os.getenv("BUILD_NAME") != None): + if os.getenv("BUILD_NAME") != None: build_name = os.getenv("BUILD_NAME") print("Using custom build name: " + build_name) import version + # NOTE: It is safe to generate this file here, since this is still executed serially f = open("core/version_generated.gen.h", "w") f.write("#define VERSION_SHORT_NAME \"" + str(version.short_name) + "\"\n") f.write("#define VERSION_NAME \"" + str(version.name) + "\"\n") f.write("#define VERSION_MAJOR " + str(version.major) + "\n") f.write("#define VERSION_MINOR " + str(version.minor) + "\n") - if (hasattr(version, 'patch')): + if hasattr(version, 'patch'): f.write("#define VERSION_PATCH " + str(version.patch) + "\n") f.write("#define VERSION_STATUS \"" + str(version.status) + "\"\n") f.write("#define VERSION_BUILD \"" + str(build_name) + "\"\n") f.write("#define VERSION_MODULE_CONFIG \"" + str(version.module_config) + module_version_string + "\"\n") - import datetime f.write("#define VERSION_YEAR " + str(datetime.datetime.now().year) + "\n") f.close() + # NOTE: It is safe to generate this file here, since this is still executed serially fhash = open("core/version_hash.gen.h", "w") githash = "" if os.path.isfile(".git/HEAD"): @@ -783,7 +63,6 @@ def update_version(module_version_string=""): def parse_cg_file(fname, uniforms, sizes, conditionals): - import re fs = open(fname, "r") line = fs.readline() @@ -797,7 +76,7 @@ def parse_cg_file(fname, uniforms, sizes, conditionals): uniforms.append(name) - if (type.find("texobj") != -1): + if type.find("texobj") != -1: sizes.append(1) else: t = re.match(r"float(\d)x(\d)", type) @@ -815,9 +94,6 @@ def parse_cg_file(fname, uniforms, sizes, conditionals): fs.close() -import glob - - def detect_modules(): module_list = [] @@ -828,9 +104,9 @@ def detect_modules(): files = glob.glob("modules/*") files.sort() # so register_module_types does not change that often, and also plugins are registered in alphabetic order for x in files: - if (not os.path.isdir(x)): + if not os.path.isdir(x): continue - if (not os.path.exists(x + "/config.py")): + if not os.path.exists(x + "/config.py"): continue x = x.replace("modules/", "") # rest of world x = x.replace("modules\\", "") # win32 @@ -862,6 +138,7 @@ void unregister_module_types() { } """ + # NOTE: It is safe to generate this file here, since this is still executed serially with open("modules/register_module_types.gen.cpp", "w") as f: f.write(modules_cpp) @@ -997,8 +274,6 @@ def use_windows_spawn_fix(self, platform=None): # changes, no multiple versions of the same object file will be present. self.Replace(ARFLAGS='q') - import subprocess - def mySubProcess(cmdline, env): startupinfo = subprocess.STARTUPINFO() @@ -1035,7 +310,6 @@ def use_windows_spawn_fix(self, platform=None): def split_lib(self, libname, src_list = None, env_lib = None): - import string env = self num = 0 @@ -1071,7 +345,6 @@ def split_lib(self, libname, src_list = None, env_lib = None): lib_list.append(lib) if len(lib_list) > 0: - import os, sys if os.name == 'posix' and sys.platform == 'msys': env.Replace(ARFLAGS=['rcsT']) lib = env_lib.add_library(libname + "_collated", lib_list) @@ -1097,7 +370,7 @@ def save_active_platforms(apnames, ap): b = pngf.read(1) str = " /* AUTOGENERATED FILE, DO NOT EDIT */ \n" str += " static const unsigned char _" + x[9:] + "_" + name + "[]={" - while(len(b) == 1): + while len(b) == 1: str += hex(ord(b)) b = pngf.read(1) if (len(b) == 1): @@ -1107,6 +380,7 @@ def save_active_platforms(apnames, ap): pngf.close() + # NOTE: It is safe to generate this file here, since this is still executed serially wf = x + "/" + name + ".gen.h" with open(wf, "w") as pngw: pngw.write(str) @@ -1248,14 +522,13 @@ def detect_visual_c_compiler_version(tools_env): return vc_chosen_compiler_str def find_visual_c_batch_file(env): - from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file + from SCons.Tool.MSCommon.vc import get_default_version, get_host_target, find_batch_file version = get_default_version(env) (host_platform, target_platform,req_target_platform) = get_host_target(env) return find_batch_file(env, version, host_platform, target_platform)[0] def generate_cpp_hint_file(filename): - import os.path if os.path.isfile(filename): # Don't overwrite an existing hint file since the user may have customized it. pass @@ -1305,13 +578,14 @@ def generate_vs_project(env, num_jobs): release_targets = ['bin\\godot.windows.opt.32.exe'] + ['bin\\godot.windows.opt.64.exe'] release_debug_targets = ['bin\\godot.windows.opt.tools.32.exe'] + ['bin\\godot.windows.opt.tools.64.exe'] targets = debug_targets + release_targets + release_debug_targets - msvproj = env.MSVSProject(target=['#godot' + env['MSVSPROJECTSUFFIX']], - incs=env.vs_incs, - srcs=env.vs_srcs, - runfile=targets, - buildtarget=targets, - auto_build_solution=1, - variant=variants) + env.MSVSProject( + target=['#godot' + env['MSVSPROJECTSUFFIX']], + incs=env.vs_incs, + srcs=env.vs_srcs, + runfile=targets, + buildtarget=targets, + auto_build_solution=1, + variant=variants) else: print("Could not locate Visual Studio batch file for setting up the build environment. Not generating VS project.") diff --git a/misc/dist/linux/godot.6 b/misc/dist/linux/godot.6 index 64495d817e..50cb420e4e 100644 --- a/misc/dist/linux/godot.6 +++ b/misc/dist/linux/godot.6 @@ -1,4 +1,4 @@ -.TH GODOT "6" "February 2018" "godot 3.1" "Games" +.TH GODOT "6" "Februaryot 2018" "godot 3.1" "Games" .SH NAME godot \- multi\-platform 2D and 3D game engine with a feature\-rich editor .SH SYNOPSIS @@ -127,6 +127,9 @@ Print the frames per second to the stdout. \fB\-s\fR, \fB\-\-script\fR <script> Run a script. .TP +\fB\-\-check\-only\fR +Only parse for errors and quit (use with --script). +.TP \fB\-\-export\fR <target> Export the project using the given export target. Export only main pack if path ends with .pck or .zip. .TP @@ -146,7 +149,7 @@ Build the scripting solutions (e.g. for C# projects). Generate JSON dump of the Godot API for GDNative bindings. .TP \fB\-\-test\fR <test> -Run a unit test ('string', 'containers', 'math', 'render', 'multimesh', 'gui', 'io', 'shaderlang', 'physics', 'oa_hash_map'). +Run a unit test ('string', 'math', 'physics', 'physics_2d', 'render', 'oa_hash_map', 'gui', 'io', 'shaderlang', 'gd_tokenizer', 'gd_parser', 'gd_compiler', 'gd_bytecode', 'image', 'ordered_hash_map'). .SH FILES XDG_DATA_CONFIG/godot/ or ~/.config/godot/ .RS @@ -156,7 +159,7 @@ XDG_DATA_HOME/godot/ or ~/.local/share/godot/ .RS Contains the default configuration and user data folders for Godot\-made games (\fIuser://\fR path), as well as export templates. .RE -XDG_DATA_CACHE/godot or ~/.cache/godot/ +XDG_DATA_CACHE/godot/ or ~/.cache/godot/ .RS Cache folder for generated thumbnails and scene previews, as well as temporary location for downloads. .RE diff --git a/misc/dist/linux/godot.appdata.xml b/misc/dist/linux/org.godotengine.Godot.appdata.xml index a381556025..9f3f9e34af 100644 --- a/misc/dist/linux/godot.appdata.xml +++ b/misc/dist/linux/org.godotengine.Godot.appdata.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- Copyright 2017-2018 Rémi Verschelde <akien@godotengine.org> --> <component type="desktop"> - <id>godot.desktop</id> + <id>org.godotengine.Godot</id> <metadata_license>CC0-1.0</metadata_license> <project_license>MIT</project_license> <name>Godot Engine</name> <summary>Multi-platform 2D and 3D game engine with a feature-rich editor</summary> + <launchable type="desktop-id">org.godotengine.Godot.desktop</launchable> <description> <p> Godot is an advanced, feature-packed, multi-platform 2D and 3D game @@ -26,6 +27,7 @@ </screenshots> <url type="homepage">https://godotengine.org</url> <url type="bugtracker">https://github.com/godotengine/godot/issues</url> + <url type="faq">http://docs.godotengine.org/en/latest/about/faq.html</url> <url type="help">http://docs.godotengine.org</url> <url type="donation">https://godotengine.org/donate</url> <url type="translate">https://hosted.weblate.org/projects/godot-engine/godot</url> diff --git a/misc/dist/linux/godot.desktop b/misc/dist/linux/org.godotengine.Godot.desktop index 974352b117..c8b99207f8 100644 --- a/misc/dist/linux/godot.desktop +++ b/misc/dist/linux/org.godotengine.Godot.desktop @@ -1,9 +1,10 @@ [Desktop Entry] Name=Godot Engine GenericName=Libre game engine -Comment=Multi-platform 2D and 3D game engine with a feature rich editor -Exec=godot -p +Comment=Multi-platform 2D and 3D game engine with a feature-rich editor +Exec=godot %f Icon=godot Terminal=false Type=Application +MimeType=application/x-godot-project; Categories=Development;IDE; diff --git a/misc/dist/linux/org.godotengine.Godot.xml b/misc/dist/linux/org.godotengine.Godot.xml new file mode 100644 index 0000000000..0572e4e54e --- /dev/null +++ b/misc/dist/linux/org.godotengine.Godot.xml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> + <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="application/x-godot-project"> + <comment>Godot Engine project</comment> + <icon name="godot" /> + <glob pattern="*.godot" weight="100" /> + </mime-type> +</mime-info> diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index bfb452d109..b004641838 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -80,7 +80,7 @@ void AreaBullet::dispatch_callbacks() { // Reverse order because I've to remove EXIT objects for (int i = overlappingObjects.size() - 1; 0 <= i; --i) { - OverlappingObjectData &otherObj = overlappingObjects[i]; + OverlappingObjectData &otherObj = overlappingObjects.write[i]; switch (otherObj.state) { case OVERLAP_STATE_ENTER: @@ -199,13 +199,13 @@ void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) { void AreaBullet::put_overlap_as_exit(int p_index) { scratch(); - overlappingObjects[p_index].state = OVERLAP_STATE_EXIT; + overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT; } void AreaBullet::put_overlap_as_inside(int p_index) { // This check is required to be sure this body was inside if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) { - overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE; + overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE; } } diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 54431f93f1..0857635492 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -169,7 +169,7 @@ real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const { } RID BulletPhysicsServer::space_create() { - SpaceBullet *space = bulletnew(SpaceBullet(false)); + SpaceBullet *space = bulletnew(SpaceBullet); CreateThenReturnRID(space_owner, space); } @@ -567,9 +567,6 @@ void BulletPhysicsServer::body_clear_shapes(RID p_body) { void BulletPhysicsServer::body_attach_object_instance_id(RID p_body, uint32_t p_ID) { CollisionObjectBullet *body = get_collisin_object(p_body); - if (!body) { - body = soft_body_owner.get(p_body); - } ERR_FAIL_COND(!body); body->set_instance_id(p_ID); @@ -710,6 +707,34 @@ Vector3 BulletPhysicsServer::body_get_applied_torque(RID p_body) const { return body->get_applied_torque(); } +void BulletPhysicsServer::body_add_central_force(RID p_body, const Vector3 &p_force) { + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_central_force(p_force); +} + +void BulletPhysicsServer::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos) { + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_force(p_force, p_pos); +} + +void BulletPhysicsServer::body_add_torque(RID p_body, const Vector3 &p_torque) { + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_torque(p_torque); +} + +void BulletPhysicsServer::body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) { + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_central_impulse(p_impulse); +} + void BulletPhysicsServer::body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) { RigidBodyBullet *body = rigid_body_owner.get(p_body); ERR_FAIL_COND(!body); @@ -853,6 +878,13 @@ RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) { CreateThenReturnRID(soft_body_owner, body); } +void BulletPhysicsServer::soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->update_visual_server(p_visual_server_handler); +} + void BulletPhysicsServer::soft_body_set_space(RID p_body, RID p_space) { SoftBodyBullet *body = soft_body_owner.get(p_body); ERR_FAIL_COND(!body); @@ -879,11 +911,11 @@ RID BulletPhysicsServer::soft_body_get_space(RID p_body) const { return space->get_self(); } -void BulletPhysicsServer::soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) { +void BulletPhysicsServer::soft_body_set_mesh(RID p_body, const REF &p_mesh) { SoftBodyBullet *body = soft_body_owner.get(p_body); ERR_FAIL_COND(!body); - body->set_trimesh_body_shape(p_indices, p_vertices, p_triangles_num); + body->set_soft_mesh(p_mesh); } void BulletPhysicsServer::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) { @@ -961,14 +993,16 @@ void BulletPhysicsServer::soft_body_set_transform(RID p_body, const Transform &p SoftBodyBullet *body = soft_body_owner.get(p_body); ERR_FAIL_COND(!body); - body->set_transform(p_transform); + body->set_soft_transform(p_transform); } -Transform BulletPhysicsServer::soft_body_get_transform(RID p_body) const { +Vector3 BulletPhysicsServer::soft_body_get_vertex_position(RID p_body, int vertex_index) const { const SoftBodyBullet *body = soft_body_owner.get(p_body); - ERR_FAIL_COND_V(!body, Transform()); + Vector3 pos; + ERR_FAIL_COND_V(!body, pos); - return body->get_transform(); + body->get_node_position(vertex_index, pos); + return pos; } void BulletPhysicsServer::soft_body_set_ray_pickable(RID p_body, bool p_enable) { @@ -983,6 +1017,154 @@ bool BulletPhysicsServer::soft_body_is_ray_pickable(RID p_body) const { return body->is_ray_pickable(); } +void BulletPhysicsServer::soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_simulation_precision(p_simulation_precision); +} + +int BulletPhysicsServer::soft_body_get_simulation_precision(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_simulation_precision(); +} + +void BulletPhysicsServer::soft_body_set_total_mass(RID p_body, real_t p_total_mass) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_total_mass(p_total_mass); +} + +real_t BulletPhysicsServer::soft_body_get_total_mass(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_total_mass(); +} + +void BulletPhysicsServer::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_linear_stiffness(p_stiffness); +} + +real_t BulletPhysicsServer::soft_body_get_linear_stiffness(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_linear_stiffness(); +} + +void BulletPhysicsServer::soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_areaAngular_stiffness(p_stiffness); +} + +real_t BulletPhysicsServer::soft_body_get_areaAngular_stiffness(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_areaAngular_stiffness(); +} + +void BulletPhysicsServer::soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_volume_stiffness(p_stiffness); +} + +real_t BulletPhysicsServer::soft_body_get_volume_stiffness(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_volume_stiffness(); +} + +void BulletPhysicsServer::soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_pressure_coefficient(p_pressure_coefficient); +} + +real_t BulletPhysicsServer::soft_body_get_pressure_coefficient(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_pressure_coefficient(); +} + +void BulletPhysicsServer::soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + return body->set_pose_matching_coefficient(p_pose_matching_coefficient); +} + +real_t BulletPhysicsServer::soft_body_get_pose_matching_coefficient(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_pose_matching_coefficient(); +} + +void BulletPhysicsServer::soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_damping_coefficient(p_damping_coefficient); +} + +real_t BulletPhysicsServer::soft_body_get_damping_coefficient(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_damping_coefficient(); +} + +void BulletPhysicsServer::soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_drag_coefficient(p_drag_coefficient); +} + +real_t BulletPhysicsServer::soft_body_get_drag_coefficient(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_drag_coefficient(); +} + +void BulletPhysicsServer::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_node_position(p_point_index, p_global_position); +} + +Vector3 BulletPhysicsServer::soft_body_get_point_global_position(RID p_body, int p_point_index) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, Vector3(0., 0., 0.)); + Vector3 pos; + body->get_node_position(p_point_index, pos); + return pos; +} + +Vector3 BulletPhysicsServer::soft_body_get_point_offset(RID p_body, int p_point_index) const { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, Vector3()); + Vector3 res; + body->get_node_offset(p_point_index, res); + return res; +} + +void BulletPhysicsServer::soft_body_remove_all_pinned_points(RID p_body) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->reset_all_node_mass(); +} + +void BulletPhysicsServer::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND(!body); + body->set_node_mass(p_point_index, p_pin ? 0 : 1); +} + +bool BulletPhysicsServer::soft_body_is_point_pinned(RID p_body, int p_point_index) { + SoftBodyBullet *body = soft_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0.f); + return body->get_node_mass(p_point_index); +} + PhysicsServer::JointType BulletPhysicsServer::joint_get_type(RID p_joint) const { JointBullet *joint = joint_owner.get(p_joint); ERR_FAIL_COND_V(!joint, JOINT_PIN); diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index e931915bba..0e858ff311 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -225,6 +225,11 @@ public: virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque); virtual Vector3 body_get_applied_torque(RID p_body) const; + virtual void body_add_central_force(RID p_body, const Vector3 &p_force); + virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos); + virtual void body_add_torque(RID p_body, const Vector3 &p_torque); + + virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse); virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse); virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse); virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity); @@ -259,10 +264,12 @@ public: virtual RID soft_body_create(bool p_init_sleeping = false); + virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler); + virtual void soft_body_set_space(RID p_body, RID p_space); virtual RID soft_body_get_space(RID p_body) const; - virtual void soft_body_set_trimesh_body_shape(RID p_body, PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num); + virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh); virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer); virtual uint32_t soft_body_get_collision_layer(RID p_body) const; @@ -277,12 +284,49 @@ public: virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant); virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const; + /// Special function. This function has bad performance virtual void soft_body_set_transform(RID p_body, const Transform &p_transform); - virtual Transform soft_body_get_transform(RID p_body) const; + virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const; virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable); virtual bool soft_body_is_ray_pickable(RID p_body) const; + virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision); + virtual int soft_body_get_simulation_precision(RID p_body); + + virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass); + virtual real_t soft_body_get_total_mass(RID p_body); + + virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness); + virtual real_t soft_body_get_linear_stiffness(RID p_body); + + virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness); + virtual real_t soft_body_get_areaAngular_stiffness(RID p_body); + + virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness); + virtual real_t soft_body_get_volume_stiffness(RID p_body); + + virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient); + virtual real_t soft_body_get_pressure_coefficient(RID p_body); + + virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient); + virtual real_t soft_body_get_pose_matching_coefficient(RID p_body); + + virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient); + virtual real_t soft_body_get_damping_coefficient(RID p_body); + + virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient); + virtual real_t soft_body_get_drag_coefficient(RID p_body); + + virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position); + virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index); + + virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const; + + virtual void soft_body_remove_all_pinned_points(RID p_body); + virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin); + virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index); + /* JOINT API */ virtual JointType joint_get_type(RID p_joint) const; diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index 57e4db708e..271cdb0223 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -111,6 +111,8 @@ void CollisionObjectBullet::setupBulletCollisionObject(btCollisionObject *p_coll void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) { exceptions.insert(p_ignoreCollisionObject->get_self()); + if (!bt_collision_object) + return; bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, true); if (space) space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher()); @@ -221,7 +223,7 @@ void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform } void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) { - ShapeWrapper &shp = shapes[p_index]; + ShapeWrapper &shp = shapes.write[p_index]; shp.shape->remove_owner(this); p_shape->add_owner(this); shp.shape = p_shape; @@ -231,8 +233,8 @@ void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) { void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) { ERR_FAIL_INDEX(p_index, get_shape_count()); - shapes[p_index].set_transform(p_transform); - on_shape_changed(shapes[p_index].shape); + shapes.write[p_index].set_transform(p_transform); + on_shape_changed(shapes.write[p_index].shape); } void RigidCollisionObjectBullet::remove_shape(ShapeBullet *p_shape) { @@ -285,7 +287,7 @@ void RigidCollisionObjectBullet::on_shape_changed(const ShapeBullet *const p_sha const int size = shapes.size(); for (int i = 0; i < size; ++i) { if (shapes[i].shape == p_shape) { - bulletdelete(shapes[i].bt_shape); + bulletdelete(shapes.write[i].bt_shape); } } on_shapes_changed(); @@ -305,7 +307,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() { // Reset shape if required if (force_shape_reset) { for (i = 0; i < shapes_size; ++i) { - shpWrapper = &shapes[i]; + shpWrapper = &shapes.write[i]; bulletdelete(shpWrapper->bt_shape); } force_shape_reset = false; @@ -314,7 +316,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() { // Insert all shapes btVector3 body_scale(get_bt_body_scale()); for (i = 0; i < shapes_size; ++i) { - shpWrapper = &shapes[i]; + shpWrapper = &shapes.write[i]; if (shpWrapper->active) { if (!shpWrapper->bt_shape) { shpWrapper->bt_shape = shpWrapper->shape->create_bt_shape(shpWrapper->scale * body_scale); @@ -332,7 +334,7 @@ void RigidCollisionObjectBullet::on_shapes_changed() { } void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled) { - shapes[p_index].active = !p_disabled; + shapes.write[p_index].active = !p_disabled; on_shapes_changed(); } @@ -346,7 +348,7 @@ void RigidCollisionObjectBullet::on_body_scale_changed() { } void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) { - ShapeWrapper &shp = shapes[p_index]; + ShapeWrapper &shp = shapes.write[p_index]; shp.shape->remove_owner(this, p_permanentlyFromThisBody); bulletdelete(shp.bt_shape); } diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp index b75f7464ab..a76b0438b4 100644 --- a/modules/bullet/register_types.cpp +++ b/modules/bullet/register_types.cpp @@ -32,6 +32,7 @@ #include "bullet_physics_server.h" #include "class_db.h" +#include "project_settings.h" /** @author AndreaCatania @@ -47,6 +48,9 @@ void register_bullet_types() { #ifndef _3D_DISABLED PhysicsServerManager::register_server("Bullet", &_createBulletPhysicsCallback); PhysicsServerManager::set_default_server("Bullet", 1); + + GLOBAL_DEF("physics/3d/active_soft_world", true); + ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/active_soft_world", PropertyInfo(Variant::BOOL, "physics/3d/active_soft_world")); #endif } diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 2fc96a77b5..9c0e802be5 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -126,6 +126,10 @@ void BulletPhysicsDirectBodyState::add_torque(const Vector3 &p_torque) { body->apply_torque(p_torque); } +void BulletPhysicsDirectBodyState::apply_central_impulse(const Vector3 &p_j) { + body->apply_central_impulse(p_j); +} + void BulletPhysicsDirectBodyState::apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) { body->apply_impulse(p_pos, p_j); } @@ -154,6 +158,10 @@ Vector3 BulletPhysicsDirectBodyState::get_contact_local_normal(int p_contact_idx return body->collisions[p_contact_idx].hitNormal; } +float BulletPhysicsDirectBodyState::get_contact_impulse(int p_contact_idx) const { + return body->collisions[p_contact_idx].appliedImpulse; +} + int BulletPhysicsDirectBodyState::get_contact_local_shape(int p_contact_idx) const { return body->collisions[p_contact_idx].local_shape; } @@ -175,7 +183,7 @@ int BulletPhysicsDirectBodyState::get_contact_collider_shape(int p_contact_idx) } Vector3 BulletPhysicsDirectBodyState::get_contact_collider_velocity_at_position(int p_contact_idx) const { - RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx]; + RigidBodyBullet::CollisionData &colDat = body->collisions.write[p_contact_idx]; btVector3 hitLocation; G_TO_B(colDat.hitLocalLocation, hitLocation); @@ -220,8 +228,8 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { continue; } - shapes[i].transform = shape_wrapper->transform; - shapes[i].transform.getOrigin() *= owner_scale; + shapes.write[i].transform = shape_wrapper->transform; + shapes.write[i].transform.getOrigin() *= owner_scale; switch (shape_wrapper->shape->get_type()) { case PhysicsServer::SHAPE_SPHERE: case PhysicsServer::SHAPE_BOX: @@ -229,11 +237,11 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { case PhysicsServer::SHAPE_CYLINDER: case PhysicsServer::SHAPE_CONVEX_POLYGON: case PhysicsServer::SHAPE_RAY: { - shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin)); + shapes.write[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin)); } break; default: WARN_PRINT("This shape is not supported to be kinematic!"); - shapes[i].shape = NULL; + shapes.write[i].shape = NULL; } } } @@ -241,7 +249,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() { void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) { for (int i = shapes.size() - 1; 0 <= i; --i) { if (shapes[i].shape) { - bulletdelete(shapes[i].shape); + bulletdelete(shapes.write[i].shape); } } shapes.resize(new_size); @@ -281,7 +289,7 @@ RigidBodyBullet::RigidBodyBullet() : areasWhereIam.resize(maxAreasWhereIam); for (int i = areasWhereIam.size() - 1; 0 <= i; --i) { - areasWhereIam[i] = NULL; + areasWhereIam.write[i] = NULL; } btBody->setSleepingThresholds(0.2, 0.2); } @@ -401,17 +409,18 @@ void RigidBodyBullet::on_collision_checker_start() { collisionsCount = 0; } -bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index) { +bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) { if (collisionsCount >= maxCollisionsDetection) { return false; } - CollisionData &cd = collisions[collisionsCount]; + CollisionData &cd = collisions.write[collisionsCount]; cd.hitLocalLocation = p_hitLocalLocation; cd.otherObject = p_otherObject; cd.hitWorldLocation = p_hitWorldLocation; cd.hitNormal = p_hitNormal; + cd.appliedImpulse = p_appliedImpulse; cd.other_object_shape = p_other_shape_index; cd.local_shape = p_local_shape_index; @@ -795,15 +804,15 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) { if (NULL == areasWhereIam[i]) { // This area has the highest priority - areasWhereIam[i] = p_area; + areasWhereIam.write[i] = p_area; break; } else { if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) { // The position was found, just shift all elements for (int j = i; j < areaWhereIamCount; ++j) { - areasWhereIam[j + 1] = areasWhereIam[j]; + areasWhereIam.write[j + 1] = areasWhereIam[j]; } - areasWhereIam[i] = p_area; + areasWhereIam.write[i] = p_area; break; } } @@ -827,7 +836,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { if (p_area == areasWhereIam[i]) { // The area was fount, just shift down all elements for (int j = i; j < areaWhereIamCount; ++j) { - areasWhereIam[j] = areasWhereIam[j + 1]; + areasWhereIam.write[j] = areasWhereIam[j + 1]; } wasTheAreaFound = true; break; @@ -840,7 +849,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) { } --areaWhereIamCount; - areasWhereIam[areaWhereIamCount] = NULL; // Even if this is not required, I clear the last element to be safe + areasWhereIam.write[areaWhereIamCount] = NULL; // Even if this is not required, I clear the last element to be safe if (PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) { scratch_space_override_modificator(); } diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index b9511243c7..f03009bce9 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -113,6 +113,7 @@ public: virtual void add_central_force(const Vector3 &p_force); virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos); virtual void add_torque(const Vector3 &p_torque); + virtual void apply_central_impulse(const Vector3 &p_impulse); virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j); virtual void apply_torque_impulse(const Vector3 &p_j); @@ -123,6 +124,7 @@ public: virtual Vector3 get_contact_local_position(int p_contact_idx) const; virtual Vector3 get_contact_local_normal(int p_contact_idx) const; + virtual float get_contact_impulse(int p_contact_idx) const; virtual int get_contact_local_shape(int p_contact_idx) const; virtual RID get_contact_collider(int p_contact_idx) const; @@ -149,6 +151,7 @@ public: Vector3 hitLocalLocation; Vector3 hitWorldLocation; Vector3 hitNormal; + float appliedImpulse; }; struct ForceIntegrationCallback { @@ -248,7 +251,7 @@ public: } bool can_add_collision() { return collisionsCount < maxCollisionsDetection; } - bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, int p_other_shape_index, int p_local_shape_index); + bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index); void assert_no_constraints(); diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index 92e7c2df98..e4c1a5f9b5 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -304,7 +304,7 @@ void ConvexPolygonShapeBullet::get_vertices(Vector<Vector3> &out_vertices) { const int n_of_vertices = vertices.size(); out_vertices.resize(n_of_vertices); for (int i = n_of_vertices - 1; 0 <= i; --i) { - B_TO_G(vertices[i], out_vertices[i]); + B_TO_G(vertices[i], out_vertices.write[i]); } } diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 5c20eb73f1..9fc7230f91 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -32,42 +32,24 @@ #include "bullet_types_converter.h" #include "bullet_utilities.h" -#include "scene/3d/immediate_geometry.h" +#include "scene/3d/soft_body.h" #include "space_bullet.h" -/** - @author AndreaCatania -*/ - SoftBodyBullet::SoftBodyBullet() : CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY), - mass(1), + total_mass(1), simulation_precision(5), - stiffness(0.5f), - pressure_coefficient(50), - damping_coefficient(0.005), - drag_coefficient(0.005), + linear_stiffness(0.5), + areaAngular_stiffness(0.5), + volume_stiffness(0.5), + pressure_coefficient(0.), + pose_matching_coefficient(0.), + damping_coefficient(0.01), + drag_coefficient(0.), bt_soft_body(NULL), - soft_shape_type(SOFT_SHAPETYPE_NONE), - isScratched(false), - soft_body_shape_data(NULL) { - - test_geometry = memnew(ImmediateGeometry); - - red_mat = Ref<SpatialMaterial>(memnew(SpatialMaterial)); - red_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true); - red_mat->set_line_width(20.0); - red_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - red_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - red_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true); - red_mat->set_albedo(Color(1, 0, 0, 1)); - test_geometry->set_material_override(red_mat); - - test_is_in_scene = false; -} + isScratched(false) {} SoftBodyBullet::~SoftBodyBullet() { - bulletdelete(soft_body_shape_data); } void SoftBodyBullet::reload_body() { @@ -80,8 +62,6 @@ void SoftBodyBullet::reload_body() { void SoftBodyBullet::set_space(SpaceBullet *p_space) { if (space) { isScratched = false; - - // Remove this object from the physics world space->remove_soft_body(this); } @@ -90,86 +70,181 @@ void SoftBodyBullet::set_space(SpaceBullet *p_space) { if (space) { space->add_soft_body(this); } - - reload_soft_body(); } -void SoftBodyBullet::dispatch_callbacks() { - if (!bt_soft_body) { +void SoftBodyBullet::dispatch_callbacks() {} + +void SoftBodyBullet::on_collision_filters_change() {} + +void SoftBodyBullet::on_collision_checker_start() {} + +void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {} + +void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {} + +void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_server_handler) { + if (!bt_soft_body) return; + + /// Update visual server vertices + const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes); + const int nodes_count = nodes.size(); + + const Vector<int> *vs_indices; + const void *vertex_position; + const void *vertex_normal; + + for (int vertex_index = 0; vertex_index < nodes_count; ++vertex_index) { + vertex_position = reinterpret_cast<const void *>(&nodes[vertex_index].m_x); + vertex_normal = reinterpret_cast<const void *>(&nodes[vertex_index].m_n); + + vs_indices = &indices_table[vertex_index]; + + const int vs_indices_size(vs_indices->size()); + for (int x = 0; x < vs_indices_size; ++x) { + p_visual_server_handler->set_vertex((*vs_indices)[x], vertex_position); + p_visual_server_handler->set_normal((*vs_indices)[x], vertex_normal); + } } - if (!test_is_in_scene) { - test_is_in_scene = true; - SceneTree::get_singleton()->get_current_scene()->add_child(test_geometry); + /// Generate AABB + btVector3 aabb_min; + btVector3 aabb_max; + bt_soft_body->getAabb(aabb_min, aabb_max); + + btVector3 size(aabb_max - aabb_min); + + AABB aabb; + B_TO_G(aabb_min, aabb.position); + B_TO_G(size, aabb.size); + + p_visual_server_handler->set_aabb(aabb); +} + +void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) { + + if (p_mesh.is_null()) + soft_mesh.unref(); + else + soft_mesh = p_mesh; + + if (soft_mesh.is_null()) { + + destroy_soft_body(); + return; } - test_geometry->clear(); - test_geometry->begin(Mesh::PRIMITIVE_LINES, NULL); - bool first = true; - Vector3 pos; - for (int i = 0; i < bt_soft_body->m_nodes.size(); ++i) { - const btSoftBody::Node &n = bt_soft_body->m_nodes[i]; - B_TO_G(n.m_x, pos); - test_geometry->add_vertex(pos); - if (!first) { - test_geometry->add_vertex(pos); - } else { - first = false; - } + Array arrays = soft_mesh->surface_get_arrays(0); + ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & VS::ARRAY_FORMAT_INDEX)); + set_trimesh_body_shape(arrays[VS::ARRAY_INDEX], arrays[VS::ARRAY_VERTEX]); +} + +void SoftBodyBullet::destroy_soft_body() { + + if (!bt_soft_body) + return; + + if (space) { + /// Remove from world before deletion + space->remove_soft_body(this); } - test_geometry->end(); + + destroyBulletCollisionObject(); + bt_soft_body = NULL; +} + +void SoftBodyBullet::set_soft_transform(const Transform &p_transform) { + reset_all_node_positions(); + move_all_nodes(p_transform); } -void SoftBodyBullet::on_collision_filters_change() { +void SoftBodyBullet::move_all_nodes(const Transform &p_transform) { + if (!bt_soft_body) + return; + btTransform bt_transf; + G_TO_B(p_transform, bt_transf); + bt_soft_body->transform(bt_transf); } -void SoftBodyBullet::on_collision_checker_start() { +void SoftBodyBullet::set_node_position(int p_node_index, const Vector3 &p_global_position) { + btVector3 bt_pos; + G_TO_B(p_global_position, bt_pos); + set_node_position(p_node_index, bt_pos); } -void SoftBodyBullet::on_enter_area(AreaBullet *p_area) { +void SoftBodyBullet::set_node_position(int p_node_index, const btVector3 &p_global_position) { + if (bt_soft_body) { + bt_soft_body->m_nodes[p_node_index].m_x = p_global_position; + } } -void SoftBodyBullet::on_exit_area(AreaBullet *p_area) { +void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) const { + if (bt_soft_body) { + B_TO_G(bt_soft_body->m_nodes[p_node_index].m_x, r_position); + } } -void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num) { +void SoftBodyBullet::get_node_offset(int p_node_index, Vector3 &r_offset) const { + if (soft_mesh.is_null()) + return; + + Array arrays = soft_mesh->surface_get_arrays(0); + PoolVector<Vector3> vertices(arrays[VS::ARRAY_VERTEX]); - TrimeshSoftShapeData *shape_data = bulletnew(TrimeshSoftShapeData); - shape_data->m_triangles_indices = p_indices; - shape_data->m_vertices = p_vertices; - shape_data->m_triangles_num = p_triangles_num; + if (0 <= p_node_index && vertices.size() > p_node_index) { + r_offset = vertices[p_node_index]; + } +} - set_body_shape_data(shape_data, SOFT_SHAPE_TYPE_TRIMESH); - reload_soft_body(); +void SoftBodyBullet::get_node_offset(int p_node_index, btVector3 &r_offset) const { + Vector3 off; + get_node_offset(p_node_index, off); + G_TO_B(off, r_offset); } -void SoftBodyBullet::set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type) { - bulletdelete(soft_body_shape_data); - soft_body_shape_data = p_soft_shape_data; - soft_shape_type = p_type; +void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) { + if (0 >= p_mass) { + pin_node(node_index); + } else { + unpin_node(node_index); + } + if (bt_soft_body) { + bt_soft_body->setMass(node_index, p_mass); + } } -void SoftBodyBullet::set_transform(const Transform &p_transform) { - transform = p_transform; +btScalar SoftBodyBullet::get_node_mass(int node_index) const { if (bt_soft_body) { - // TODO the softbody set new transform considering the current transform as center of world - // like if it's local transform, so I must fix this by setting nwe transform considering the old - btTransform bt_trans; - G_TO_B(transform, bt_trans); - //bt_soft_body->transform(bt_trans); + return bt_soft_body->getMass(node_index); + } else { + return -1 == search_node_pinned(node_index) ? 1 : 0; } } -const Transform &SoftBodyBullet::get_transform() const { - return transform; +void SoftBodyBullet::reset_all_node_mass() { + if (bt_soft_body) { + for (int i = pinned_nodes.size() - 1; 0 <= i; --i) { + bt_soft_body->setMass(pinned_nodes[i], 1); + } + } + pinned_nodes.resize(0); } -void SoftBodyBullet::get_first_node_origin(btVector3 &p_out_origin) const { - if (bt_soft_body && bt_soft_body->m_nodes.size()) { - p_out_origin = bt_soft_body->m_nodes[0].m_x; - } else { - p_out_origin.setZero(); +void SoftBodyBullet::reset_all_node_positions() { + if (soft_mesh.is_null()) + return; + + Array arrays = soft_mesh->surface_get_arrays(0); + PoolVector<Vector3> vs_vertices(arrays[VS::ARRAY_VERTEX]); + PoolVector<Vector3>::Read vs_vertices_read = vs_vertices.read(); + + for (int vertex_index = bt_soft_body->m_nodes.size() - 1; 0 <= vertex_index; --vertex_index) { + + G_TO_B(vs_vertices_read[indices_table[vertex_index][0]], bt_soft_body->m_nodes[vertex_index].m_x); + + bt_soft_body->m_nodes[vertex_index].m_q = bt_soft_body->m_nodes[vertex_index].m_x; + bt_soft_body->m_nodes[vertex_index].m_v = btVector3(0, 0, 0); + bt_soft_body->m_nodes[vertex_index].m_f = btVector3(0, 0, 0); } } @@ -181,22 +256,34 @@ void SoftBodyBullet::set_activation_state(bool p_active) { } } -void SoftBodyBullet::set_mass(real_t p_val) { +void SoftBodyBullet::set_total_mass(real_t p_val) { if (0 >= p_val) { p_val = 1; } - mass = p_val; + total_mass = p_val; if (bt_soft_body) { - bt_soft_body->setTotalMass(mass); + bt_soft_body->setTotalMass(total_mass); } } -void SoftBodyBullet::set_stiffness(real_t p_val) { - stiffness = p_val; +void SoftBodyBullet::set_linear_stiffness(real_t p_val) { + linear_stiffness = p_val; if (bt_soft_body) { - mat0->m_kAST = stiffness; - mat0->m_kLST = stiffness; - mat0->m_kVST = stiffness; + mat0->m_kLST = linear_stiffness; + } +} + +void SoftBodyBullet::set_areaAngular_stiffness(real_t p_val) { + areaAngular_stiffness = p_val; + if (bt_soft_body) { + mat0->m_kAST = areaAngular_stiffness; + } +} + +void SoftBodyBullet::set_volume_stiffness(real_t p_val) { + volume_stiffness = p_val; + if (bt_soft_body) { + mat0->m_kVST = volume_stiffness; } } @@ -204,6 +291,9 @@ void SoftBodyBullet::set_simulation_precision(int p_val) { simulation_precision = p_val; if (bt_soft_body) { bt_soft_body->m_cfg.piterations = simulation_precision; + bt_soft_body->m_cfg.viterations = simulation_precision; + bt_soft_body->m_cfg.diterations = simulation_precision; + bt_soft_body->m_cfg.citerations = simulation_precision; } } @@ -214,6 +304,13 @@ void SoftBodyBullet::set_pressure_coefficient(real_t p_val) { } } +void SoftBodyBullet::set_pose_matching_coefficient(real_t p_val) { + pose_matching_coefficient = p_val; + if (bt_soft_body) { + bt_soft_body->m_cfg.kMT = pose_matching_coefficient; + } +} + void SoftBodyBullet::set_damping_coefficient(real_t p_val) { damping_coefficient = p_val; if (bt_soft_body) { @@ -228,89 +325,156 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) { } } -void SoftBodyBullet::reload_soft_body() { - +void SoftBodyBullet::set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices) { + /// Assert the current soft body is destroyed destroy_soft_body(); - create_soft_body(); - if (bt_soft_body) { + /// Parse visual server indices to physical indices. + /// Merge all overlapping vertices and create a map of physical vertices to visual server - // TODO the softbody set new transform considering the current transform as center of world - // like if it's local transform, so I must fix this by setting nwe transform considering the old - btTransform bt_trans; - G_TO_B(transform, bt_trans); - bt_soft_body->transform(bt_trans); + { + /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map + Vector<int> vs_indices_to_physics_table; - bt_soft_body->generateBendingConstraints(2, mat0); - mat0->m_kAST = stiffness; - mat0->m_kLST = stiffness; - mat0->m_kVST = stiffness; + { // Map vertices + indices_table.resize(0); - bt_soft_body->m_cfg.piterations = simulation_precision; - bt_soft_body->m_cfg.kDP = damping_coefficient; - bt_soft_body->m_cfg.kDG = drag_coefficient; - bt_soft_body->m_cfg.kPR = pressure_coefficient; - bt_soft_body->setTotalMass(mass); - } - if (space) { - // TODO remove this please - space->add_soft_body(this); - } -} + int index = 0; + Map<Vector3, int> unique_vertices; -void SoftBodyBullet::create_soft_body() { - if (!space || !soft_body_shape_data) { - return; - } - ERR_FAIL_COND(!space->is_using_soft_world()); - switch (soft_shape_type) { - case SOFT_SHAPE_TYPE_TRIMESH: { - TrimeshSoftShapeData *trimesh_data = static_cast<TrimeshSoftShapeData *>(soft_body_shape_data); - - Vector<int> indices; - Vector<btScalar> vertices; - - int i; - const int indices_size = trimesh_data->m_triangles_indices.size(); - const int vertices_size = trimesh_data->m_vertices.size(); - indices.resize(indices_size); - vertices.resize(vertices_size * 3); - - PoolVector<int>::Read i_r = trimesh_data->m_triangles_indices.read(); - for (i = 0; i < indices_size; ++i) { - indices[i] = i_r[i]; + const int vs_vertices_size(p_vertices.size()); + + PoolVector<Vector3>::Read p_vertices_read = p_vertices.read(); + + for (int vs_vertex_index = 0; vs_vertex_index < vs_vertices_size; ++vs_vertex_index) { + + Map<Vector3, int>::Element *e = unique_vertices.find(p_vertices_read[vs_vertex_index]); + int vertex_id; + if (e) { + // Already rxisting + vertex_id = e->value(); + } else { + // Create new one + unique_vertices[p_vertices_read[vs_vertex_index]] = vertex_id = index++; + indices_table.push_back(Vector<int>()); + } + + indices_table.write[vertex_id].push_back(vs_vertex_index); + vs_indices_to_physics_table.push_back(vertex_id); + } + } + + const int indices_map_size(indices_table.size()); + + Vector<btScalar> bt_vertices; + + { // Parse vertices to bullet + + bt_vertices.resize(indices_map_size * 3); + PoolVector<Vector3>::Read p_vertices_read = p_vertices.read(); + + for (int i = 0; i < indices_map_size; ++i) { + bt_vertices.write[3 * i + 0] = p_vertices_read[indices_table[i][0]].x; + bt_vertices.write[3 * i + 1] = p_vertices_read[indices_table[i][0]].y; + bt_vertices.write[3 * i + 2] = p_vertices_read[indices_table[i][0]].z; } - i_r = PoolVector<int>::Read(); + } + + Vector<int> bt_triangles; + const int triangles_size(p_indices.size() / 3); + + { // Parse indices + + bt_triangles.resize(triangles_size * 3); + + PoolVector<int>::Read p_indices_read = p_indices.read(); - PoolVector<Vector3>::Read f_r = trimesh_data->m_vertices.read(); - for (int j = i = 0; i < vertices_size; ++i, j += 3) { - vertices[j + 0] = f_r[i][0]; - vertices[j + 1] = f_r[i][1]; - vertices[j + 2] = f_r[i][2]; + for (int i = 0; i < triangles_size; ++i) { + bt_triangles.write[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]]; + bt_triangles.write[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]]; + bt_triangles.write[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]]; } - f_r = PoolVector<Vector3>::Read(); + } - bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(*space->get_soft_body_world_info(), vertices.ptr(), indices.ptr(), trimesh_data->m_triangles_num); - } break; - default: - ERR_PRINT("Shape type not supported"); - return; + btSoftBodyWorldInfo fake_world_info; + bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false); + setup_soft_body(); } +} + +void SoftBodyBullet::setup_soft_body() { + + if (!bt_soft_body) + return; + // Soft body setup setupBulletCollisionObject(bt_soft_body); - bt_soft_body->getCollisionShape()->setMargin(0.001f); + bt_soft_body->m_worldInfo = NULL; // Remove fake world info + bt_soft_body->getCollisionShape()->setMargin(0.01); bt_soft_body->setCollisionFlags(bt_soft_body->getCollisionFlags() & (~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT))); + + // Space setup + if (space) { + space->add_soft_body(this); + } + mat0 = bt_soft_body->appendMaterial(); + + // Assign soft body data + bt_soft_body->generateBendingConstraints(2, mat0); + + mat0->m_kLST = linear_stiffness; + mat0->m_kAST = areaAngular_stiffness; + mat0->m_kVST = volume_stiffness; + + // Clusters allow to have Soft vs Soft collision but doesn't work well right now + + //bt_soft_body->m_cfg.kSRHR_CL = 1;// Soft vs rigid hardness [0,1] (cluster only) + //bt_soft_body->m_cfg.kSKHR_CL = 1;// Soft vs kinematic hardness [0,1] (cluster only) + //bt_soft_body->m_cfg.kSSHR_CL = 1;// Soft vs soft hardness [0,1] (cluster only) + //bt_soft_body->m_cfg.kSR_SPLT_CL = 1; // Soft vs rigid impulse split [0,1] (cluster only) + //bt_soft_body->m_cfg.kSK_SPLT_CL = 1; // Soft vs kinematic impulse split [0,1] (cluster only) + //bt_soft_body->m_cfg.kSS_SPLT_CL = 1; // Soft vs Soft impulse split [0,1] (cluster only) + //bt_soft_body->m_cfg.collisions = btSoftBody::fCollision::CL_SS + btSoftBody::fCollision::CL_RS + btSoftBody::fCollision::VF_SS; + //bt_soft_body->generateClusters(64); + + bt_soft_body->m_cfg.piterations = simulation_precision; + bt_soft_body->m_cfg.viterations = simulation_precision; + bt_soft_body->m_cfg.diterations = simulation_precision; + bt_soft_body->m_cfg.citerations = simulation_precision; + bt_soft_body->m_cfg.kDP = damping_coefficient; + bt_soft_body->m_cfg.kDG = drag_coefficient; + bt_soft_body->m_cfg.kPR = pressure_coefficient; + bt_soft_body->m_cfg.kMT = pose_matching_coefficient; + bt_soft_body->setTotalMass(total_mass); + + btSoftBodyHelpers::ReoptimizeLinkOrder(bt_soft_body); + bt_soft_body->updateBounds(); + + // Set pinned nodes + for (int i = pinned_nodes.size() - 1; 0 <= i; --i) { + bt_soft_body->setMass(pinned_nodes[i], 0); + } } -void SoftBodyBullet::destroy_soft_body() { - if (space) { - /// This step is required to assert that the body is not into the world during deletion - /// This step is required since to change the body shape the body must be re-created. - /// Here is handled the case when the body is assigned into a world and the body - /// shape is changed. - space->remove_soft_body(this); +void SoftBodyBullet::pin_node(int p_node_index) { + if (-1 == search_node_pinned(p_node_index)) { + pinned_nodes.push_back(p_node_index); } - destroyBulletCollisionObject(); - bt_soft_body = NULL; +} + +void SoftBodyBullet::unpin_node(int p_node_index) { + const int id = search_node_pinned(p_node_index); + if (-1 != id) { + pinned_nodes.remove(id); + } +} + +int SoftBodyBullet::search_node_pinned(int p_node_index) const { + for (int i = pinned_nodes.size() - 1; 0 <= i; --i) { + if (p_node_index == pinned_nodes[i]) { + return i; + } + } + return -1; } diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h index 9895643b84..c775193584 100644 --- a/modules/bullet/soft_body_bullet.h +++ b/modules/bullet/soft_body_bullet.h @@ -40,7 +40,10 @@ #define x11_None 0L #endif -#include <BulletSoftBody/btSoftBodyHelpers.h> +#include "BulletSoftBody/btSoftBodyHelpers.h" +#include "collision_object_bullet.h" +#include "scene/resources/mesh.h" +#include "servers/physics_server.h" #ifdef x11_None /// This is required to re add the macro None defined by x11 compiler @@ -52,39 +55,34 @@ @author AndreaCatania */ -struct SoftShapeData {}; -struct TrimeshSoftShapeData : public SoftShapeData { - PoolVector<int> m_triangles_indices; - PoolVector<Vector3> m_vertices; - int m_triangles_num; -}; - class SoftBodyBullet : public CollisionObjectBullet { -public: - enum SoftShapeType { - SOFT_SHAPETYPE_NONE = 0, - SOFT_SHAPE_TYPE_TRIMESH - }; private: btSoftBody *bt_soft_body; + Vector<Vector<int> > indices_table; btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody - SoftShapeType soft_shape_type; bool isScratched; - SoftShapeData *soft_body_shape_data; + Ref<Mesh> soft_mesh; - Transform transform; int simulation_precision; - real_t mass; - real_t stiffness; // [0,1] + real_t total_mass; + real_t linear_stiffness; // [0,1] + real_t areaAngular_stiffness; // [0,1] + real_t volume_stiffness; // [0,1] real_t pressure_coefficient; // [-inf,+inf] + real_t pose_matching_coefficient; // [0,1] real_t damping_coefficient; // [0,1] real_t drag_coefficient; // [0,1] + Vector<int> pinned_nodes; - class ImmediateGeometry *test_geometry; // TODO remove this please - Ref<SpatialMaterial> red_mat; // TODO remove this please - bool test_is_in_scene; // TODO remove this please + // Other property to add + //btScalar kVC; // Volume conversation coefficient [0,+inf] + //btScalar kDF; // Dynamic friction coefficient [0,1] + //btScalar kMT; // Pose matching coefficient [0,1] + //btScalar kCHR; // Rigid contacts hardness [0,1] + //btScalar kKHR; // Kinetic contacts hardness [0,1] + //btScalar kSHR; // Soft contacts hardness [0,1] public: SoftBodyBullet(); @@ -101,39 +99,64 @@ public: _FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; } - void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices, int p_triangles_num); - void set_body_shape_data(SoftShapeData *p_soft_shape_data, SoftShapeType p_type); + void update_visual_server(class SoftBodyVisualServerHandler *p_visual_server_handler); - void set_transform(const Transform &p_transform); - /// This function doesn't return the exact COM transform. - /// It returns the origin only of first node (vertice) of current soft body - /// --- - /// The soft body doesn't have a fixed center of mass, but is a group of nodes (vertices) - /// that each has its own position in the world. - /// For this reason return the correct COM is not so simple and must be calculate - /// Check this to improve this function http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=8803 - const Transform &get_transform() const; - void get_first_node_origin(btVector3 &p_out_origin) const; + void set_soft_mesh(const Ref<Mesh> &p_mesh); + void destroy_soft_body(); + + // Special function. This function has bad performance + void set_soft_transform(const Transform &p_transform); + + void move_all_nodes(const Transform &p_transform); + void set_node_position(int node_index, const Vector3 &p_global_position); + void set_node_position(int node_index, const btVector3 &p_global_position); + void get_node_position(int node_index, Vector3 &r_position) const; + // Heavy function, Please cache this info + void get_node_offset(int node_index, Vector3 &r_offset) const; + // Heavy function, Please cache this info + void get_node_offset(int node_index, btVector3 &r_offset) const; + + void set_node_mass(int node_index, btScalar p_mass); + btScalar get_node_mass(int node_index) const; + void reset_all_node_mass(); + void reset_all_node_positions(); void set_activation_state(bool p_active); - void set_mass(real_t p_val); - _FORCE_INLINE_ real_t get_mass() const { return mass; } - void set_stiffness(real_t p_val); - _FORCE_INLINE_ real_t get_stiffness() const { return stiffness; } + void set_total_mass(real_t p_val); + _FORCE_INLINE_ real_t get_total_mass() const { return total_mass; } + + void set_linear_stiffness(real_t p_val); + _FORCE_INLINE_ real_t get_linear_stiffness() const { return linear_stiffness; } + + void set_areaAngular_stiffness(real_t p_val); + _FORCE_INLINE_ real_t get_areaAngular_stiffness() const { return areaAngular_stiffness; } + + void set_volume_stiffness(real_t p_val); + _FORCE_INLINE_ real_t get_volume_stiffness() const { return volume_stiffness; } + void set_simulation_precision(int p_val); _FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; } + void set_pressure_coefficient(real_t p_val); _FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; } + + void set_pose_matching_coefficient(real_t p_val); + _FORCE_INLINE_ real_t get_pose_matching_coefficient() const { return pose_matching_coefficient; } + void set_damping_coefficient(real_t p_val); _FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; } + void set_drag_coefficient(real_t p_val); _FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; } private: - void reload_soft_body(); - void create_soft_body(); - void destroy_soft_body(); + void set_trimesh_body_shape(PoolVector<int> p_indices, PoolVector<Vector3> p_vertices); + void setup_soft_body(); + + void pin_node(int p_node_index); + void unpin_node(int p_node_index); + int search_node_pinned(int p_node_index) const; }; #endif // SOFT_BODY_BULLET_H diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 971fd39509..97228a972f 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -36,6 +36,7 @@ #include "constraint_bullet.h" #include "godot_collision_configuration.h" #include "godot_collision_dispatcher.h" +#include "project_settings.h" #include "rigid_body_bullet.h" #include "servers/physics_server.h" #include "soft_body_bullet.h" @@ -325,7 +326,7 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_ } } -SpaceBullet::SpaceBullet(bool p_create_soft_world) : +SpaceBullet::SpaceBullet() : broadphase(NULL), dispatcher(NULL), solver(NULL), @@ -338,7 +339,7 @@ SpaceBullet::SpaceBullet(bool p_create_soft_world) : gravityMagnitude(10), contactDebugCount(0) { - create_empty_world(p_create_soft_world); + create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true)); direct_access = memnew(BulletPhysicsDirectSpaceState(this)); } @@ -355,6 +356,7 @@ void SpaceBullet::flush_queries() { } void SpaceBullet::step(real_t p_delta_time) { + delta_time = p_delta_time; dynamicsWorld->stepSimulation(p_delta_time, 0, 0); } @@ -483,6 +485,7 @@ void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) { void SpaceBullet::add_soft_body(SoftBodyBullet *p_body) { if (is_using_soft_world()) { if (p_body->get_bt_soft_body()) { + p_body->get_bt_soft_body()->m_worldInfo = get_soft_body_world_info(); static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->addSoftBody(p_body->get_bt_soft_body(), p_body->get_collision_layer(), p_body->get_collision_mask()); } } else { @@ -494,6 +497,7 @@ void SpaceBullet::remove_soft_body(SoftBodyBullet *p_body) { if (is_using_soft_world()) { if (p_body->get_bt_soft_body()) { static_cast<btSoftRigidDynamicsWorld *>(dynamicsWorld)->removeSoftBody(p_body->get_bt_soft_body()); + p_body->get_bt_soft_body()->m_worldInfo = NULL; } } } @@ -549,7 +553,13 @@ BulletPhysicsDirectSpaceState *SpaceBullet::get_direct_state() { } btScalar calculateGodotCombinedRestitution(const btCollisionObject *body0, const btCollisionObject *body1) { - return MAX(body0->getRestitution(), body1->getRestitution()); + + return CLAMP(body0->getRestitution() + body1->getRestitution(), 0, 1); +} + +btScalar calculateGodotCombinedFriction(const btCollisionObject *body0, const btCollisionObject *body1) { + + return ABS(MIN(body0->getFriction(), body1->getFriction())); } void SpaceBullet::create_empty_world(bool p_create_soft_world) { @@ -585,6 +595,7 @@ void SpaceBullet::create_empty_world(bool p_create_soft_world) { ghostPairCallback = bulletnew(btGhostPairCallback); godotFilterCallback = bulletnew(GodotFilterCallback); gCalculateCombinedRestitutionCallback = &calculateGodotCombinedRestitution; + gCalculateCombinedFrictionCallback = &calculateGodotCombinedFriction; dynamicsWorld->setWorldUserInfo(this); @@ -645,7 +656,7 @@ void SpaceBullet::check_ghost_overlaps() { /// 1. Reset all states for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) { - AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i]; + AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i]; // This check prevent the overwrite of ENTER state // if this function is called more times before dispatchCallbacks if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) { @@ -754,19 +765,20 @@ void SpaceBullet::check_body_collision() { Vector3 collisionWorldPosition; Vector3 collisionLocalPosition; Vector3 normalOnB; + float appliedImpulse = pt.m_appliedImpulse; B_TO_G(pt.m_normalWorldOnB, normalOnB); if (bodyA->can_add_collision()) { B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition); /// pt.m_localPointB Doesn't report the exact point in local space B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, pt.m_index1, pt.m_index0); + bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0); } if (bodyB->can_add_collision()) { B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition); /// pt.m_localPointA Doesn't report the exact point in local space B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition); - bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, pt.m_index0, pt.m_index1); + bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1); } #ifdef DEBUG_ENABLED diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index a6c2786878..6b86fc2f03 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -84,7 +84,7 @@ public: }; class SpaceBullet : public RIDBullet { -private: + friend class AreaBullet; friend void onBulletTickCallback(btDynamicsWorld *world, btScalar timeStep); friend class BulletPhysicsDirectSpaceState; @@ -109,12 +109,14 @@ private: Vector<Vector3> contactDebug; int contactDebugCount; + real_t delta_time; public: - SpaceBullet(bool p_create_soft_world); + SpaceBullet(); virtual ~SpaceBullet(); void flush_queries(); + real_t get_delta_time() { return delta_time; } void step(real_t p_delta_time); _FORCE_INLINE_ btBroadphaseInterface *get_broadphase() { return broadphase; } @@ -162,7 +164,7 @@ public: contactDebugCount = 0; } _FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) { - if (contactDebugCount < contactDebug.size()) contactDebug[contactDebugCount++] = p_contact; + if (contactDebugCount < contactDebug.size()) contactDebug.write[contactDebugCount++] = p_contact; } _FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; } _FORCE_INLINE_ int get_debug_contact_count() { return contactDebugCount; } diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 4e6e701bfd..87c2caec0d 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -62,7 +62,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo faces.resize(p_vertices.size() / 3); for (int i = 0; i < faces.size(); i++) { - Face &f = faces[i]; + Face &f = faces.write[i]; f.vertices[0] = rv[i * 3 + 0]; f.vertices[1] = rv[i * 3 + 1]; f.vertices[2] = rv[i * 3 + 2]; @@ -101,7 +101,7 @@ void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const Poo materials.resize(material_map.size()); for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) { - materials[E->get()] = E->key(); + materials.write[E->get()] = E->key(); } _regen_face_aabbs(); @@ -111,10 +111,10 @@ void CSGBrush::_regen_face_aabbs() { for (int i = 0; i < faces.size(); i++) { - faces[i].aabb.position = faces[i].vertices[0]; - faces[i].aabb.expand_to(faces[i].vertices[1]); - faces[i].aabb.expand_to(faces[i].vertices[2]); - faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros + faces.write[i].aabb.position = faces[i].vertices[0]; + faces.write[i].aabb.expand_to(faces[i].vertices[1]); + faces.write[i].aabb.expand_to(faces[i].vertices[2]); + faces.write[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros } } @@ -125,7 +125,7 @@ void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) { for (int i = 0; i < faces.size(); i++) { for (int j = 0; j < 3; j++) { - faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]); + faces.write[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]); } } @@ -341,7 +341,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ new_edge.points[0] = edges[i].points[0]; new_edge.points[1] = point_idx; new_edge.outer = edges[i].outer; - edges[i].points[0] = point_idx; + edges.write[i].points[0] = point_idx; edges.insert(i, new_edge); i++; //skip newly inserted edge base_edges++; //will need an extra one in the base triangle @@ -637,7 +637,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in int to_point = e.edge_point; int current_edge = e.edge; - edge_process[e.edge] = true; //mark as processed + edge_process.write[e.edge] = true; //mark as processed int limit = p_poly.points.size() * 4; //avoid infinite recursion @@ -708,7 +708,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in prev_point = to_point; to_point = next_point; - edge_process[next_edge] = true; //mark this edge as processed + edge_process.write[next_edge] = true; //mark this edge as processed current_edge = next_edge; limit--; @@ -792,13 +792,13 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build //none processed by default for (int i = 0; i < edge_process.size(); i++) { - edge_process[i] = false; + edge_process.write[i] = false; } //put edges in points, so points can go through them for (int i = 0; i < p_poly.edges.size(); i++) { - vertex_process[p_poly.edges[i].points[0]].push_back(i); - vertex_process[p_poly.edges[i].points[1]].push_back(i); + vertex_process.write[p_poly.edges[i].points[0]].push_back(i); + vertex_process.write[p_poly.edges[i].points[1]].push_back(i); } Vector<PolyPoints> polys; @@ -854,7 +854,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline); if (outline.size() > 1) { - polys[intersect_poly].holes.push_back(outline); + polys.write[intersect_poly].holes.push_back(outline); } } _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys); @@ -953,18 +953,18 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build //duplicate point int insert_at = with_outline_vertex; - polys[i].points.insert(insert_at, polys[i].points[insert_at]); + polys.write[i].points.insert(insert_at, polys[i].points[insert_at]); insert_at++; //insert all others, outline should be backwards (must check) int holesize = polys[i].holes[j].size(); for (int k = 0; k <= holesize; k++) { int idx = (from_hole_vertex + k) % holesize; - polys[i].points.insert(insert_at, polys[i].holes[j][idx]); + polys.write[i].points.insert(insert_at, polys[i].holes[j][idx]); insert_at++; } added_hole = true; - polys[i].holes.remove(j); + polys.write[i].holes.remove(j); break; //got rid of hole, break and continue } } @@ -980,7 +980,7 @@ void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const Build Vector<Vector2> vertices; vertices.resize(polys[i].points.size()); for (int j = 0; j < vertices.size(); j++) { - vertices[j] = p_poly.points[polys[i].points[j]].point; + vertices.write[j] = p_poly.points[polys[i].points[j]].point; } Vector<int> indices = Geometry::triangulate_polygon(vertices); @@ -1267,7 +1267,7 @@ void CSGBrushOperation::MeshMerge::mark_inside_faces() { int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i); if (intersections & 1) { - faces[i].inside = true; + faces.write[i].inside = true; } } } @@ -1419,13 +1419,13 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A if (mesh_merge.faces[i].inside) continue; for (int j = 0; j < 3; j++) { - result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; - result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + result.faces.write[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces.write[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; } - result.faces[outside_count].smooth = mesh_merge.faces[i].smooth; - result.faces[outside_count].invert = mesh_merge.faces[i].invert; - result.faces[outside_count].material = mesh_merge.faces[i].material_idx; + result.faces.write[outside_count].smooth = mesh_merge.faces[i].smooth; + result.faces.write[outside_count].invert = mesh_merge.faces[i].invert; + result.faces.write[outside_count].material = mesh_merge.faces[i].material_idx; outside_count++; } @@ -1451,13 +1451,13 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A if (!mesh_merge.faces[i].inside) continue; for (int j = 0; j < 3; j++) { - result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; - result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + result.faces.write[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces.write[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; } - result.faces[inside_count].smooth = mesh_merge.faces[i].smooth; - result.faces[inside_count].invert = mesh_merge.faces[i].invert; - result.faces[inside_count].material = mesh_merge.faces[i].material_idx; + result.faces.write[inside_count].smooth = mesh_merge.faces[i].smooth; + result.faces.write[inside_count].invert = mesh_merge.faces[i].invert; + result.faces.write[inside_count].material = mesh_merge.faces[i].material_idx; inside_count++; } @@ -1489,19 +1489,19 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A continue; for (int j = 0; j < 3; j++) { - result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; - result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + result.faces.write[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces.write[face_count].uvs[j] = mesh_merge.faces[i].uvs[j]; } if (mesh_merge.faces[i].from_b) { //invert facing of insides of B - SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]); - SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]); + SWAP(result.faces.write[face_count].vertices[1], result.faces.write[face_count].vertices[2]); + SWAP(result.faces.write[face_count].uvs[1], result.faces.write[face_count].uvs[2]); } - result.faces[face_count].smooth = mesh_merge.faces[i].smooth; - result.faces[face_count].invert = mesh_merge.faces[i].invert; - result.faces[face_count].material = mesh_merge.faces[i].material_idx; + result.faces.write[face_count].smooth = mesh_merge.faces[i].smooth; + result.faces.write[face_count].invert = mesh_merge.faces[i].invert; + result.faces.write[face_count].material = mesh_merge.faces[i].material_idx; face_count++; } @@ -1513,6 +1513,6 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A //updatelist of materials result.materials.resize(mesh_merge.materials.size()); for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) { - result.materials[E->get()] = E->key(); + result.materials.write[E->get()] = E->key(); } } diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index 2150320c4a..f9744c72af 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -32,7 +32,16 @@ /////////// -String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const { +CSGShapeSpatialGizmoPlugin::CSGShapeSpatialGizmoPlugin() { + + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1)); + create_material("shape_material", gizmo_color); + create_handle_material("handles"); +} + +String CSGShapeSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + + CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node()); if (Object::cast_to<CSGSphere>(cs)) { @@ -57,7 +66,9 @@ String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const { return ""; } -Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const { +Variant CSGShapeSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const { + + CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node()); if (Object::cast_to<CSGSphere>(cs)) { @@ -89,10 +100,12 @@ Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const { return Variant(); } -void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { +void CSGShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) { + + CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node()); Transform gt = cs->get_global_transform(); - gt.orthonormalize(); + //gt.orthonormalize(); Transform gi = gt.affine_inverse(); Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -170,7 +183,9 @@ void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 s->set_outer_radius(d); } } -void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void CSGShapeSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { + + CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node()); if (Object::cast_to<CSGSphere>(cs)) { CSGSphere *s = Object::cast_to<CSGSphere>(cs); @@ -260,12 +275,26 @@ void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bo ur->commit_action(); } } -void CSGShapeSpatialGizmo::redraw() { +bool CSGShapeSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial); +} - clear(); +String CSGShapeSpatialGizmoPlugin::get_name() const { + return "CSGShapes"; +} + +bool CSGShapeSpatialGizmoPlugin::is_selectable_when_hidden() const { + return true; +} - Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg"); - Ref<Material> material = create_material("shape_material", gizmo_color); +void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + CSGShape *cs = Object::cast_to<CSGShape>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref<Material> material = get_material("shape_material", p_gizmo); + Ref<Material> handles_material = get_material("handles"); PoolVector<Vector3> faces = cs->get_brush_faces(); @@ -278,14 +307,14 @@ void CSGShapeSpatialGizmo::redraw() { int f = i / 6; for (int j = 0; j < 3; j++) { int j_n = (j + 1) % 3; - lines[i + j * 2 + 0] = r[f * 3 + j]; - lines[i + j * 2 + 1] = r[f * 3 + j_n]; + lines.write[i + j * 2 + 0] = r[f * 3 + j]; + lines.write[i + j * 2 + 1] = r[f * 3 + j_n]; } } } - add_lines(lines, material); - add_collision_segments(lines); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); if (Object::cast_to<CSGSphere>(cs)) { CSGSphere *s = Object::cast_to<CSGSphere>(cs); @@ -293,7 +322,7 @@ void CSGShapeSpatialGizmo::redraw() { float r = s->get_radius(); Vector<Vector3> handles; handles.push_back(Vector3(r, 0, 0)); - add_handles(handles); + p_gizmo->add_handles(handles, handles_material); } if (Object::cast_to<CSGBox>(cs)) { @@ -303,7 +332,7 @@ void CSGShapeSpatialGizmo::redraw() { handles.push_back(Vector3(s->get_width(), 0, 0)); handles.push_back(Vector3(0, s->get_height(), 0)); handles.push_back(Vector3(0, 0, s->get_depth())); - add_handles(handles); + p_gizmo->add_handles(handles, handles_material); } if (Object::cast_to<CSGCylinder>(cs)) { @@ -312,7 +341,7 @@ void CSGShapeSpatialGizmo::redraw() { Vector<Vector3> handles; handles.push_back(Vector3(s->get_radius(), 0, 0)); handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); - add_handles(handles); + p_gizmo->add_handles(handles, handles_material); } if (Object::cast_to<CSGTorus>(cs)) { @@ -321,25 +350,11 @@ void CSGShapeSpatialGizmo::redraw() { Vector<Vector3> handles; handles.push_back(Vector3(s->get_inner_radius(), 0, 0)); handles.push_back(Vector3(s->get_outer_radius(), 0, 0)); - add_handles(handles); - } -} -CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) { - - cs = p_cs; - set_spatial_node(p_cs); -} - -Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) { - if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) { - Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial))); - return csg; + p_gizmo->add_handles(handles, handles_material); } - - return Ref<SpatialEditorGizmo>(); } EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) { - - EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1)); + Ref<CSGShapeSpatialGizmoPlugin> gizmo_plugin = Ref<CSGShapeSpatialGizmoPlugin>(memnew(CSGShapeSpatialGizmoPlugin)); + SpatialEditor::get_singleton()->register_gizmo_plugin(gizmo_plugin); } diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index 68e916823b..d65d1f58c1 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -35,25 +35,27 @@ #include "editor/editor_plugin.h" #include "editor/spatial_editor_gizmos.h" -class CSGShapeSpatialGizmo : public EditorSpatialGizmo { +class CSGShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo); - - CSGShape *cs; + GDCLASS(CSGShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: - virtual String get_handle_name(int p_idx) const; - virtual Variant get_handle_value(int p_idx) const; - virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); - virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - void redraw(); - CSGShapeSpatialGizmo(CSGShape *p_cs = NULL); + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + bool is_selectable_when_hidden() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + String get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; + void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); + void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel); + + CSGShapeSpatialGizmoPlugin(); }; class EditorPluginCSG : public EditorPlugin { GDCLASS(EditorPluginCSG, EditorPlugin) public: - virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); EditorPluginCSG(EditorNode *p_editor); }; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 67cc7e1ba2..9f2171a82a 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -177,7 +177,7 @@ void CSGShape::_update_shape() { Vector<int> face_count; face_count.resize(n->materials.size() + 1); for (int i = 0; i < face_count.size(); i++) { - face_count[i] = 0; + face_count.write[i] = 0; } for (int i = 0; i < n->faces.size(); i++) { @@ -200,7 +200,7 @@ void CSGShape::_update_shape() { } } - face_count[idx]++; + face_count.write[idx]++; } Vector<ShapeUpdateSurface> surfaces; @@ -210,18 +210,18 @@ void CSGShape::_update_shape() { //create arrays for (int i = 0; i < surfaces.size(); i++) { - surfaces[i].vertices.resize(face_count[i] * 3); - surfaces[i].normals.resize(face_count[i] * 3); - surfaces[i].uvs.resize(face_count[i] * 3); - surfaces[i].last_added = 0; + surfaces.write[i].vertices.resize(face_count[i] * 3); + surfaces.write[i].normals.resize(face_count[i] * 3); + surfaces.write[i].uvs.resize(face_count[i] * 3); + surfaces.write[i].last_added = 0; if (i != surfaces.size() - 1) { - surfaces[i].material = n->materials[i]; + surfaces.write[i].material = n->materials[i]; } - surfaces[i].verticesw = surfaces[i].vertices.write(); - surfaces[i].normalsw = surfaces[i].normals.write(); - surfaces[i].uvsw = surfaces[i].uvs.write(); + surfaces.write[i].verticesw = surfaces.write[i].vertices.write(); + surfaces.write[i].normalsw = surfaces.write[i].normals.write(); + surfaces.write[i].uvsw = surfaces.write[i].uvs.write(); } //fill arrays @@ -281,7 +281,7 @@ void CSGShape::_update_shape() { surfaces[idx].normalsw[last + order[j]] = normal; } - surfaces[idx].last_added += 3; + surfaces.write[idx].last_added += 3; } } @@ -290,9 +290,9 @@ void CSGShape::_update_shape() { for (int i = 0; i < surfaces.size(); i++) { - surfaces[i].verticesw = PoolVector<Vector3>::Write(); - surfaces[i].normalsw = PoolVector<Vector3>::Write(); - surfaces[i].uvsw = PoolVector<Vector2>::Write(); + surfaces.write[i].verticesw = PoolVector<Vector3>::Write(); + surfaces.write[i].normalsw = PoolVector<Vector3>::Write(); + surfaces.write[i].uvsw = PoolVector<Vector2>::Write(); if (surfaces[i].last_added == 0) continue; diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 88768829d7..25b7f2472d 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -666,7 +666,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer * while (total) { for (size_t i = 0; i < inBufferCount; i++) { int to_copy = MIN(total, int(inBuffers[i].dataLength)); - copymem(&enet->src_compressor_mem[ofs], inBuffers[i].data, to_copy); + copymem(&enet->src_compressor_mem.write[ofs], inBuffers[i].data, to_copy); ofs += to_copy; total -= to_copy; } diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index 8a674bc8c1..ddfa7af771 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -98,6 +98,33 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f Image::Format img_format = p_img->get_format(); Image::DetectChannels detected_channels = p_img->get_detected_channels(); + if (p_source == Image::COMPRESS_SOURCE_LAYERED) { + //keep what comes in + switch (p_img->get_format()) { + case Image::FORMAT_L8: { + detected_channels = Image::DETECTED_L; + } break; + case Image::FORMAT_LA8: { + detected_channels = Image::DETECTED_LA; + } break; + case Image::FORMAT_R8: { + detected_channels = Image::DETECTED_R; + } break; + case Image::FORMAT_RG8: { + detected_channels = Image::DETECTED_RG; + } break; + case Image::FORMAT_RGB8: { + detected_channels = Image::DETECTED_RGB; + } break; + case Image::FORMAT_RGBA8: + case Image::FORMAT_RGBA4444: + case Image::FORMAT_RGBA5551: { + detected_channels = Image::DETECTED_RGBA; + } break; + default: {} + } + } + if (p_source == Image::COMPRESS_SOURCE_SRGB && (detected_channels == Image::DETECTED_R || detected_channels == Image::DETECTED_RG)) { //R and RG do not support SRGB detected_channels = Image::DETECTED_RGB; @@ -147,7 +174,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f PoolVector<uint8_t>::Read r = img->get_data().read(); - int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps()); int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0); PoolVector<uint8_t> dst_data; diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 116a86b27b..46b2a832f1 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -17,268 +17,19 @@ SConscript("net/SCsub") SConscript("arvr/SCsub") SConscript("pluginscript/SCsub") -def _spaced(e): - return e if e[-1] == '*' else e + ' ' +from platform_methods import run_in_subprocess +import gdnative_builders -def _build_gdnative_api_struct_header(api): - gdnative_api_init_macro = [ - '\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;' - ] - - for ext in api['extensions']: - name = ext['name'] - gdnative_api_init_macro.append( - '\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name)) - - gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;') - gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ') - gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {') - - for ext in api['extensions']: - name = ext['name'] - gdnative_api_init_macro.append( - '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type']) - gdnative_api_init_macro.append( - '\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)' - ' _gdnative_wrapper_api_struct->extensions[i];'.format(name)) - gdnative_api_init_macro.append('\t\t\t\tbreak;') - gdnative_api_init_macro.append('\t\t}') - gdnative_api_init_macro.append('\t}') - - out = [ - '/* THIS FILE IS GENERATED DO NOT EDIT */', - '#ifndef GODOT_GDNATIVE_API_STRUCT_H', - '#define GODOT_GDNATIVE_API_STRUCT_H', - '', - '#include <gdnative/gdnative.h>', - '#include <android/godot_android.h>', - '#include <arvr/godot_arvr.h>', - '#include <nativescript/godot_nativescript.h>', - '#include <pluginscript/godot_pluginscript.h>', - '', - '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)', - '', - '#ifdef __cplusplus', - 'extern "C" {', - '#endif', - '', - 'enum GDNATIVE_API_TYPES {', - '\tGDNATIVE_' + api['core']['type'] + ',' - ] - - for ext in api['extensions']: - out += ['\tGDNATIVE_EXT_' + ext['type'] + ','] - - out += ['};', ''] - - - def generate_extension_struct(name, ext, include_version=True): - ret_val = [] - if ext['next']: - ret_val += generate_extension_struct(name, ext['next']) - - ret_val += [ - 'typedef struct godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct {', - '\tunsigned int type;', - '\tgodot_gdnative_api_version version;', - '\tconst godot_gdnative_api_struct *next;' - ] - - for funcdef in ext['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - ret_val += ['} godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct;', ''] - - return ret_val - - - for ext in api['extensions']: - name = ext['name'] - out += generate_extension_struct(name, ext, False) - - out += [ - 'typedef struct godot_gdnative_core_api_struct {', - '\tunsigned int type;', - '\tgodot_gdnative_api_version version;', - '\tconst godot_gdnative_api_struct *next;', - '\tunsigned int num_extensions;', - '\tconst godot_gdnative_api_struct **extensions;', - ] - - for funcdef in api['core']['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - out += [ - '} godot_gdnative_core_api_struct;', - '', - '#ifdef __cplusplus', - '}', - '#endif', - '', - '#endif // GODOT_GDNATIVE_API_STRUCT_H', - '' - ] - return '\n'.join(out) - -def _build_gdnative_api_struct_source(api): - out = [ - '/* THIS FILE IS GENERATED DO NOT EDIT */', - '', - '#include <gdnative_api_struct.gen.h>', - '' - ] - - def get_extension_struct_name(name, ext, include_version=True): - return 'godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct' - - def get_extension_struct_instance_name(name, ext, include_version=True): - return 'api_extension_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_struct' - - def get_extension_struct_definition(name, ext, include_version=True): - - ret_val = [] - - if ext['next']: - ret_val += get_extension_struct_definition(name, ext['next']) - - ret_val += [ - 'extern const ' + get_extension_struct_name(name, ext, include_version) + ' ' + get_extension_struct_instance_name(name, ext, include_version) + ' = {', - '\tGDNATIVE_EXT_' + ext['type'] + ',', - '\t{' + str(ext['version']['major']) + ', ' + str(ext['version']['minor']) + '},', - '\t' + ('NULL' if not ext['next'] else ('(const godot_gdnative_api_struct *)&' + get_extension_struct_instance_name(name, ext['next']))) + ',' - ] - - for funcdef in ext['api']: - ret_val.append('\t%s,' % funcdef['name']) - - ret_val += ['};\n'] - - return ret_val - - for ext in api['extensions']: - name = ext['name'] - out += get_extension_struct_definition(name, ext, False) - - out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {'] - - for ext in api['extensions']: - name = ext['name'] - out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,'] - - out += ['};\n'] - - out += [ - 'extern const godot_gdnative_core_api_struct api_struct = {', - '\tGDNATIVE_' + api['core']['type'] + ',', - '\t{' + str(api['core']['version']['major']) + ', ' + str(api['core']['version']['minor']) + '},', - '\tNULL,', - '\t' + str(len(api['extensions'])) + ',', - '\tgdnative_extensions_pointers,', - ] - - for funcdef in api['core']['api']: - out.append('\t%s,' % funcdef['name']) - out.append('};\n') - - return '\n'.join(out) - -def build_gdnative_api_struct(target, source, env): - import json - from collections import OrderedDict - - with open(source[0].path, 'r') as fd: - api = json.load(fd) - - header, source = target - with open(header.path, 'w') as fd: - fd.write(_build_gdnative_api_struct_header(api)) - with open(source.path, 'w') as fd: - fd.write(_build_gdnative_api_struct_source(api)) _, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], - 'gdnative_api.json', build_gdnative_api_struct) + 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_api_struct)) gdn_env.add_source_files(env.modules_sources, [gensource]) env.use_ptrcall = True -def _build_gdnative_wrapper_code(api): - out = [ - '/* THIS FILE IS GENERATED DO NOT EDIT */', - '', - '#include <gdnative/gdnative.h>', - '#include <nativescript/godot_nativescript.h>', - '#include <pluginscript/godot_pluginscript.h>', - '#include <arvr/godot_arvr.h>', - '', - '#include <gdnative_api_struct.gen.h>', - '', - '#ifdef __cplusplus', - 'extern "C" {', - '#endif', - '', - 'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;', - ] - - for ext in api['extensions']: - name = ext['name'] - out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;') - - out += [''] - - for funcdef in api['core']['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) - - return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' - return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');' - - out.append(return_line) - out.append('}') - out.append('') - - for ext in api['extensions']: - name = ext['name'] - for funcdef in ext['api']: - args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) - out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) - - args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) - - return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' - return_line += '_gdnative_wrapper_' + name + '_api_struct->' + funcdef['name'] + '(' + args + ');' - - out.append(return_line) - out.append('}') - out.append('') - - out += [ - '#ifdef __cplusplus', - '}', - '#endif' - ] - - return '\n'.join(out) - - -def build_gdnative_wrapper_code(target, source, env): - import json - with open(source[0].path, 'r') as fd: - api = json.load(fd) - - wrapper_file = target[0] - with open(wrapper_file.path, 'w') as fd: - fd.write(_build_gdnative_wrapper_code(api)) - - - if ARGUMENTS.get('gdnative_wrapper', False): -#build wrapper code - gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', build_gdnative_wrapper_code) + gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) gd_wrapper_env = env.Clone() gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index 385c020a78..b525725107 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -125,7 +125,7 @@ bool ARVRInterfaceGDNative::is_stereo() { return stereo; } -bool ARVRInterfaceGDNative::is_initialized() { +bool ARVRInterfaceGDNative::is_initialized() const { bool initialized; ERR_FAIL_COND_V(interface == NULL, false); diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h index e50be6e196..26d0cdf9f4 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ b/modules/gdnative/arvr/arvr_interface_gdnative.h @@ -59,7 +59,7 @@ public: virtual StringName get_name() const; virtual int get_capabilities() const; - virtual bool is_initialized(); + virtual bool is_initialized() const; virtual bool initialize(); virtual void uninitialize(); diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index c5b37d35b4..701a13d32f 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -9,9 +9,11 @@ def get_doc_classes(): "ARVRInterfaceGDNative", "GDNative", "GDNativeLibrary", + "MultiplayerPeerGDNative", "NativeScript", "PacketPeerGDNative", "PluginScript", + "StreamPeerGDNative", ] def get_doc_path(): diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml new file mode 100644 index 0000000000..4433179726 --- /dev/null +++ b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="MultiplayerPeerGDNative" inherits="NetworkedMultiplayerPeer" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml new file mode 100644 index 0000000000..0ae54bc9c7 --- /dev/null +++ b/modules/gdnative/doc_classes/PacketPeerGDNative.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="PacketPeerGDNative" inherits="PacketPeer" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml new file mode 100644 index 0000000000..d86cd2c25a --- /dev/null +++ b/modules/gdnative/doc_classes/StreamPeerGDNative.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="StreamPeerGDNative" inherits="StreamPeer" category="Core" version="3.1"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <demos> + </demos> + <methods> + </methods> + <constants> + </constants> +</class> diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index e7ebcc73af..0acd6c27d8 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -277,7 +277,7 @@ void GDNative::set_library(Ref<GDNativeLibrary> p_library) { library = p_library; } -Ref<GDNativeLibrary> GDNative::get_library() { +Ref<GDNativeLibrary> GDNative::get_library() const { return library; } @@ -373,7 +373,7 @@ bool GDNative::initialize() { if (library->should_load_once() && !GDNativeLibrary::loaded_libraries->has(lib_path)) { Vector<Ref<GDNative> > gdnatives; gdnatives.resize(1); - gdnatives[0] = Ref<GDNative>(this); + gdnatives.write[0] = Ref<GDNative>(this); GDNativeLibrary::loaded_libraries->insert(lib_path, gdnatives); } @@ -428,7 +428,7 @@ bool GDNative::terminate() { return true; } -bool GDNative::is_initialized() { +bool GDNative::is_initialized() const { return initialized; } @@ -442,7 +442,7 @@ Vector<StringName> GDNativeCallRegistry::get_native_call_types() { size_t idx = 0; for (Map<StringName, native_call_cb>::Element *E = native_calls.front(); E; E = E->next(), idx++) { - call_types[idx] = E->key(); + call_types.write[idx] = E->key(); } return call_types; @@ -474,7 +474,7 @@ Variant GDNative::call_native(StringName p_native_call_type, StringName p_proced return res; } -Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) { +Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional) const { if (!initialized) { ERR_PRINT("No valid library handle, can't get symbol from GDNative object"); diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index b17bb94f1c..148f85723e 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -148,16 +148,16 @@ public: static void _bind_methods(); void set_library(Ref<GDNativeLibrary> p_library); - Ref<GDNativeLibrary> get_library(); + Ref<GDNativeLibrary> get_library() const; - bool is_initialized(); + bool is_initialized() const; bool initialize(); bool terminate(); Variant call_native(StringName p_native_call_type, StringName p_procedure_name, Array p_arguments = Array()); - Error get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional = true); + Error get_symbol(StringName p_procedure_name, void *&r_handle, bool p_optional = true) const; }; class GDNativeLibraryResourceLoader : public ResourceFormatLoader { diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp index 7f5dbc12be..8ca57392a3 100644 --- a/modules/gdnative/gdnative/string.cpp +++ b/modules/gdnative/gdnative/string.cpp @@ -207,7 +207,7 @@ godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_arra Array *keys_proxy = (Array *)p_keys; keys.resize(keys_proxy->size()); for (int i = 0; i < keys_proxy->size(); i++) { - keys[i] = (*keys_proxy)[i]; + keys.write[i] = (*keys_proxy)[i]; } return self->findmk(keys); @@ -220,7 +220,7 @@ godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot Array *keys_proxy = (Array *)p_keys; keys.resize(keys_proxy->size()); for (int i = 0; i < keys_proxy->size(); i++) { - keys[i] = (*keys_proxy)[i]; + keys.write[i] = (*keys_proxy)[i]; } return self->findmk(keys, p_from); @@ -233,7 +233,7 @@ godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, co Array *keys_proxy = (Array *)p_keys; keys.resize(keys_proxy->size()); for (int i = 0; i < keys_proxy->size(); i++) { - keys[i] = (*keys_proxy)[i]; + keys.write[i] = (*keys_proxy)[i]; } return self->findmk(keys, p_from, r_key); @@ -696,7 +696,7 @@ godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const Array *splitter_proxy = (Array *)p_splitters; splitters.resize(splitter_proxy->size()); for (int i = 0; i < splitter_proxy->size(); i++) { - splitters[i] = (*splitter_proxy)[i]; + splitters.write[i] = (*splitter_proxy)[i]; } godot_array result; @@ -719,7 +719,7 @@ godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string * Array *splitter_proxy = (Array *)p_splitters; splitters.resize(splitter_proxy->size()); for (int i = 0; i < splitter_proxy->size(); i++) { - splitters[i] = (*splitter_proxy)[i]; + splitters.write[i] = (*splitter_proxy)[i]; } godot_array result; @@ -774,7 +774,7 @@ godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const g Array *splitter_proxy = (Array *)p_splitters; splitters.resize(splitter_proxy->size()); for (int i = 0; i < splitter_proxy->size(); i++) { - splitters[i] = (*splitter_proxy)[i]; + splitters.write[i] = (*splitter_proxy)[i]; } godot_array result; @@ -797,7 +797,7 @@ godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_ Array *splitter_proxy = (Array *)p_splitters; splitters.resize(splitter_proxy->size()); for (int i = 0; i < splitter_proxy->size(); i++) { - splitters[i] = (*splitter_proxy)[i]; + splitters.write[i] = (*splitter_proxy)[i]; } godot_array result; diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py new file mode 100644 index 0000000000..8a1cd049af --- /dev/null +++ b/modules/gdnative/gdnative_builders.py @@ -0,0 +1,261 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import json +from platform_methods import subprocess_main + + +def _spaced(e): + return e if e[-1] == '*' else e + ' ' + + +def _build_gdnative_api_struct_header(api): + gdnative_api_init_macro = [ + '\textern const godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct;' + ] + + for ext in api['extensions']: + name = ext['name'] + gdnative_api_init_macro.append( + '\textern const godot_gdnative_ext_{0}_api_struct *_gdnative_wrapper_{0}_api_struct;'.format(name)) + + gdnative_api_init_macro.append('\t_gdnative_wrapper_api_struct = options->api_struct;') + gdnative_api_init_macro.append('\tfor (unsigned int i = 0; i < _gdnative_wrapper_api_struct->num_extensions; i++) { ') + gdnative_api_init_macro.append('\t\tswitch (_gdnative_wrapper_api_struct->extensions[i]->type) {') + + for ext in api['extensions']: + name = ext['name'] + gdnative_api_init_macro.append( + '\t\t\tcase GDNATIVE_EXT_%s:' % ext['type']) + gdnative_api_init_macro.append( + '\t\t\t\t_gdnative_wrapper_{0}_api_struct = (godot_gdnative_ext_{0}_api_struct *)' + ' _gdnative_wrapper_api_struct->extensions[i];'.format(name)) + gdnative_api_init_macro.append('\t\t\t\tbreak;') + gdnative_api_init_macro.append('\t\t}') + gdnative_api_init_macro.append('\t}') + + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '#ifndef GODOT_GDNATIVE_API_STRUCT_H', + '#define GODOT_GDNATIVE_API_STRUCT_H', + '', + '#include <gdnative/gdnative.h>', + '#include <android/godot_android.h>', + '#include <arvr/godot_arvr.h>', + '#include <nativescript/godot_nativescript.h>', + '#include <pluginscript/godot_pluginscript.h>', + '', + '#define GDNATIVE_API_INIT(options) do { \\\n' + ' \\\n'.join(gdnative_api_init_macro) + ' \\\n } while (0)', + '', + '#ifdef __cplusplus', + 'extern "C" {', + '#endif', + '', + 'enum GDNATIVE_API_TYPES {', + '\tGDNATIVE_' + api['core']['type'] + ',' + ] + + for ext in api['extensions']: + out += ['\tGDNATIVE_EXT_' + ext['type'] + ','] + + out += ['};', ''] + + def generate_extension_struct(name, ext, include_version=True): + ret_val = [] + if ext['next']: + ret_val += generate_extension_struct(name, ext['next']) + + ret_val += [ + 'typedef struct godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct {', + '\tunsigned int type;', + '\tgodot_gdnative_api_version version;', + '\tconst godot_gdnative_api_struct *next;' + ] + + for funcdef in ext['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + ret_val.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + ret_val += ['} godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct;', ''] + + return ret_val + + for ext in api['extensions']: + name = ext['name'] + out += generate_extension_struct(name, ext, False) + + out += [ + 'typedef struct godot_gdnative_core_api_struct {', + '\tunsigned int type;', + '\tgodot_gdnative_api_version version;', + '\tconst godot_gdnative_api_struct *next;', + '\tunsigned int num_extensions;', + '\tconst godot_gdnative_api_struct **extensions;', + ] + + for funcdef in api['core']['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + out.append('\t%s(*%s)(%s);' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + out += [ + '} godot_gdnative_core_api_struct;', + '', + '#ifdef __cplusplus', + '}', + '#endif', + '', + '#endif // GODOT_GDNATIVE_API_STRUCT_H', + '' + ] + return '\n'.join(out) + + +def _build_gdnative_api_struct_source(api): + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '', + '#include <gdnative_api_struct.gen.h>', + '' + ] + + def get_extension_struct_name(name, ext, include_version=True): + return 'godot_gdnative_ext_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_api_struct' + + def get_extension_struct_instance_name(name, ext, include_version=True): + return 'api_extension_' + name + ('' if not include_version else ('_{0}_{1}'.format(ext['version']['major'], ext['version']['minor']))) + '_struct' + + def get_extension_struct_definition(name, ext, include_version=True): + + ret_val = [] + + if ext['next']: + ret_val += get_extension_struct_definition(name, ext['next']) + + ret_val += [ + 'extern const ' + get_extension_struct_name(name, ext, include_version) + ' ' + get_extension_struct_instance_name(name, ext, include_version) + ' = {', + '\tGDNATIVE_EXT_' + ext['type'] + ',', + '\t{' + str(ext['version']['major']) + ', ' + str(ext['version']['minor']) + '},', + '\t' + ('NULL' if not ext['next'] else ('(const godot_gdnative_api_struct *)&' + get_extension_struct_instance_name(name, ext['next']))) + ',' + ] + + for funcdef in ext['api']: + ret_val.append('\t%s,' % funcdef['name']) + + ret_val += ['};\n'] + + return ret_val + + for ext in api['extensions']: + name = ext['name'] + out += get_extension_struct_definition(name, ext, False) + + out += ['', 'const godot_gdnative_api_struct *gdnative_extensions_pointers[] = {'] + + for ext in api['extensions']: + name = ext['name'] + out += ['\t(godot_gdnative_api_struct *)&api_extension_' + name + '_struct,'] + + out += ['};\n'] + + out += [ + 'extern const godot_gdnative_core_api_struct api_struct = {', + '\tGDNATIVE_' + api['core']['type'] + ',', + '\t{' + str(api['core']['version']['major']) + ', ' + str(api['core']['version']['minor']) + '},', + '\tNULL,', + '\t' + str(len(api['extensions'])) + ',', + '\tgdnative_extensions_pointers,', + ] + + for funcdef in api['core']['api']: + out.append('\t%s,' % funcdef['name']) + out.append('};\n') + + return '\n'.join(out) + + +def build_gdnative_api_struct(target, source, env): + + with open(source[0], 'r') as fd: + api = json.load(fd) + + header, source = target + with open(header, 'w') as fd: + fd.write(_build_gdnative_api_struct_header(api)) + with open(source, 'w') as fd: + fd.write(_build_gdnative_api_struct_source(api)) + + +def _build_gdnative_wrapper_code(api): + out = [ + '/* THIS FILE IS GENERATED DO NOT EDIT */', + '', + '#include <gdnative/gdnative.h>', + '#include <nativescript/godot_nativescript.h>', + '#include <pluginscript/godot_pluginscript.h>', + '#include <arvr/godot_arvr.h>', + '', + '#include <gdnative_api_struct.gen.h>', + '', + '#ifdef __cplusplus', + 'extern "C" {', + '#endif', + '', + 'godot_gdnative_core_api_struct *_gdnative_wrapper_api_struct = 0;', + ] + + for ext in api['extensions']: + name = ext['name'] + out.append('godot_gdnative_ext_' + name + '_api_struct *_gdnative_wrapper_' + name + '_api_struct = 0;') + + out += [''] + + for funcdef in api['core']['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) + + return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' + return_line += '_gdnative_wrapper_api_struct->' + funcdef['name'] + '(' + args + ');' + + out.append(return_line) + out.append('}') + out.append('') + + for ext in api['extensions']: + name = ext['name'] + for funcdef in ext['api']: + args = ', '.join(['%s%s' % (_spaced(t), n) for t, n in funcdef['arguments']]) + out.append('%s%s(%s) {' % (_spaced(funcdef['return_type']), funcdef['name'], args)) + + args = ', '.join(['%s' % n for t, n in funcdef['arguments']]) + + return_line = '\treturn ' if funcdef['return_type'] != 'void' else '\t' + return_line += '_gdnative_wrapper_' + name + '_api_struct->' + funcdef['name'] + '(' + args + ');' + + out.append(return_line) + out.append('}') + out.append('') + + out += [ + '#ifdef __cplusplus', + '}', + '#endif' + ] + + return '\n'.join(out) + + +def build_gdnative_wrapper_code(target, source, env): + with open(source[0], 'r') as fd: + api = json.load(fd) + + wrapper_file = target[0] + with open(wrapper_file, 'w') as fd: + fd.write(_build_gdnative_wrapper_code(api)) + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp index 4012e821bb..70ca8d68b8 100644 --- a/modules/gdnative/nativescript/api_generator.cpp +++ b/modules/gdnative/nativescript/api_generator.cpp @@ -110,7 +110,6 @@ struct ClassAPI { bool is_singleton; bool is_instanciable; // @Unclear - bool is_creatable; bool is_reference; List<MethodAPI> methods; @@ -385,7 +384,6 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) { source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n"); source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n"); // @Unclear - // source.push_back(String("\t\t\"createable\": ") + (api.is_creatable ? "true" : "false") + ",\n"); source.push_back("\t\t\"constants\": {\n"); for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) { diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 5e093109d5..0f3b497c94 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -149,7 +149,10 @@ Ref<Script> NativeScript::get_base_script() const { if (!script_data) return Ref<Script>(); - Ref<NativeScript> ns = Ref<NativeScript>(NSL->create_script()); + NativeScript *script = (NativeScript *)NSL->create_script(); + Ref<NativeScript> ns = Ref<NativeScript>(script); + ERR_FAIL_COND_V(!ns.is_valid(), Ref<Script>()); + ns->set_class_name(script_data->base); ns->set_library(get_library()); return ns; @@ -553,12 +556,17 @@ bool NativeScriptInstance::set(const StringName &p_name, const Variant &p_value) Variant name = p_name; const Variant *args[2] = { &name, &p_value }; - E->get().method.method((godot_object *)owner, + godot_variant result; + result = E->get().method.method((godot_object *)owner, E->get().method.method_data, userdata, 2, (godot_variant **)args); - return true; + bool handled = *(Variant *)&result; + godot_variant_destroy(&result); + if (handled) { + return true; + } } script_data = script_data->base_data; @@ -593,10 +601,9 @@ bool NativeScriptInstance::get(const StringName &p_name, Variant &r_ret) const { (godot_variant **)args); r_ret = *(Variant *)&result; godot_variant_destroy(&result); - if (r_ret.get_type() == Variant::NIL) { - return false; + if (r_ret.get_type() != Variant::NIL) { + return true; } - return true; } script_data = script_data->base_data; @@ -1053,7 +1060,7 @@ Ref<Script> NativeScriptLanguage::get_template(const String &p_class_name, const s->set_class_name(p_class_name); return Ref<NativeScript>(s); } -bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const { +bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { return true; } @@ -1154,8 +1161,8 @@ int NativeScriptLanguage::register_binding_functions(godot_instance_binding_func } // set the functions - binding_functions[idx].first = true; - binding_functions[idx].second = p_binding_functions; + binding_functions.write[idx].first = true; + binding_functions.write[idx].second = p_binding_functions; return idx; } @@ -1170,7 +1177,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) { binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]); } - binding_functions[p_idx].first = false; + binding_functions.write[p_idx].first = false; if (binding_functions[p_idx].second.free_func) binding_functions[p_idx].second.free_func(binding_functions[p_idx].second.data); @@ -1196,7 +1203,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec binding_data->resize(p_idx + 1); for (int i = old_size; i <= p_idx; i++) { - (*binding_data)[i] = NULL; + (*binding_data).write[i] = NULL; } } @@ -1205,7 +1212,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name()); // no binding data yet, soooooo alloc new one \o/ - (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); + (*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); } return (*binding_data)[p_idx]; @@ -1218,7 +1225,7 @@ void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) { binding_data->resize(binding_functions.size()); for (int i = 0; i < binding_functions.size(); i++) { - (*binding_data)[i] = NULL; + (*binding_data).write[i] = NULL; } binding_instances.insert(binding_data); diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index 1b39b63ad9..688ed295db 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -295,7 +295,7 @@ public: virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines = NULL) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index 816b0f0cab..2b538c4a36 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -108,7 +108,7 @@ Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const return script; } -bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const { +bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { PoolStringArray functions; if (_desc.validate) { bool ret = _desc.validate( diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index 2443e31361..c4df6f3a33 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -74,7 +74,7 @@ public: virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index d18297f2f8..ae1a218392 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -341,10 +341,10 @@ void register_gdnative_types() { Ref<GDNativeLibrary> lib = ResourceLoader::load(path); - singleton_gdnatives[i].instance(); - singleton_gdnatives[i]->set_library(lib); + singleton_gdnatives.write[i].instance(); + singleton_gdnatives.write[i]->set_library(lib); - if (!singleton_gdnatives[i]->initialize()) { + if (!singleton_gdnatives.write[i]->initialize()) { // Can't initialize. Don't make a native_call then continue; } @@ -374,7 +374,7 @@ void unregister_gdnative_types() { continue; } - singleton_gdnatives[i]->terminate(); + singleton_gdnatives.write[i]->terminate(); } singleton_gdnatives.clear(); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8bd29ffc55..ef6a42f145 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -596,6 +596,13 @@ Error GDScript::reload(bool p_keep_state) { return err; } } +#if DEBUG_ENABLED + for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { + String msg = "Script warning: " + E->get().get_name() + " (" + path + ") line " + itos(E->get().line) + ": "; + msg += E->get().get_message(); + WARN_PRINTS(msg); + } +#endif valid = true; @@ -730,7 +737,7 @@ Error GDScript::load_byte_code(const String &p_path) { Vector<uint8_t> key; key.resize(32); for (int i = 0; i < key.size(); i++) { - key[i] = script_encryption_key[i]; + key.write[i] = script_encryption_key[i]; } Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_READ); ERR_FAIL_COND_V(err, err); @@ -941,7 +948,7 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { if (!E->get().data_type.is_type(p_value)) { return false; // Type mismatch } - members[E->get().index] = p_value; + members.write[E->get().index] = p_value; } return true; } @@ -1270,7 +1277,7 @@ void GDScriptInstance::reload_members() { if (member_indices_cache.has(E->key())) { Variant value = members[member_indices_cache[E->key()]]; - new_members[E->get().index] = value; + new_members.write[E->get().index] = value; } } @@ -1320,7 +1327,7 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va if (globals.has(p_name)) { //overwrite existing - global_array[globals[p_name]] = p_value; + global_array.write[globals[p_name]] = p_value; return; } globals[p_name] = global_array.size(); @@ -1867,6 +1874,162 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b return String(); } +#ifdef DEBUG_ENABLED +String GDScriptWarning::get_message() const { + +#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String()); + + switch (code) { + case UNASSIGNED_VARIABLE_OP_ASSIGN: { + CHECK_SYMBOLS(1); + return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; + } break; + case UNASSIGNED_VARIABLE: { + CHECK_SYMBOLS(1); + return "The variable '" + symbols[0] + "' was used but never assigned a value."; + } break; + case UNUSED_VARIABLE: { + CHECK_SYMBOLS(1); + return "The local variable '" + symbols[0] + "' is declared but never used in the block."; + } break; + case UNUSED_CLASS_VARIABLE: { + CHECK_SYMBOLS(1); + return "The class variable '" + symbols[0] + "' is declared but never used in the script."; + } break; + case UNUSED_ARGUMENT: { + CHECK_SYMBOLS(2); + return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'."; + } break; + case UNREACHABLE_CODE: { + CHECK_SYMBOLS(1); + return "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; + } break; + case STANDALONE_EXPRESSION: { + return "Standalone expression (the line has no effect)."; + } break; + case VOID_ASSIGNMENT: { + CHECK_SYMBOLS(1); + return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; + } break; + case NARROWING_CONVERSION: { + return "Narrowing coversion (float is converted to int and lose precision)."; + } break; + case FUNCTION_MAY_YIELD: { + CHECK_SYMBOLS(1); + return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; + } break; + case VARIABLE_CONFLICTS_FUNCTION: { + CHECK_SYMBOLS(1); + return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; + } break; + case FUNCTION_CONFLICTS_VARIABLE: { + CHECK_SYMBOLS(1); + return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; + } break; + case FUNCTION_CONFLICTS_CONSTANT: { + CHECK_SYMBOLS(1); + return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; + } break; + case INCOMPATIBLE_TERNARY: { + return "Values of the ternary conditional are not mutually compatible."; + } break; + case UNUSED_SIGNAL: { + CHECK_SYMBOLS(1); + return "The signal '" + symbols[0] + "' is declared but never emitted."; + } break; + case RETURN_VALUE_DISCARDED: { + CHECK_SYMBOLS(1); + return "The function '" + symbols[0] + "()' returns a value, but this value is never used."; + } break; + case PROPERTY_USED_AS_FUNCTION: { + CHECK_SYMBOLS(2); + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; + } break; + case CONSTANT_USED_AS_FUNCTION: { + CHECK_SYMBOLS(2); + return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; + } break; + case FUNCTION_USED_AS_PROPERTY: { + CHECK_SYMBOLS(2); + return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; + } break; + case INTEGER_DIVISION: { + return "Integer division, decimal part will be discarded."; + } break; + case UNSAFE_PROPERTY_ACCESS: { + CHECK_SYMBOLS(2); + return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + } break; + case UNSAFE_METHOD_ACCESS: { + CHECK_SYMBOLS(2); + return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; + } break; + case UNSAFE_CAST: { + CHECK_SYMBOLS(1); + return "The value is cast to '" + symbols[0] + "' but has an unkown type."; + } break; + case UNSAFE_CALL_ARGUMENT: { + CHECK_SYMBOLS(4); + return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; + } break; + } + ERR_EXPLAIN("Invalid GDScript waring code: " + get_name_from_code(code)); + ERR_FAIL_V(String()); + +#undef CHECK_SYMBOLS +} + +String GDScriptWarning::get_name() const { + return get_name_from_code(code); +} + +String GDScriptWarning::get_name_from_code(Code p_code) { + ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String()); + + static const char *names[] = { + "UNASSIGNED_VARIABLE", + "UNASSIGNED_VARIABLE_OP_ASSIGN", + "UNUSED_VARIABLE", + "UNUSED_CLASS_VARIABLE", + "UNUSED_ARGUMENT", + "UNREACHABLE_CODE", + "STANDALONE_EXPRESSION", + "VOID_ASSIGNMENT", + "NARROWING_CONVERSION", + "FUNCTION_MAY_YIELD", + "VARIABLE_CONFLICTS_FUNCTION", + "FUNCTION_CONFLICTS_VARIABLE", + "FUNCTION_CONFLICTS_CONSTANT", + "INCOMPATIBLE_TERNARY", + "UNUSED_SIGNAL", + "RETURN_VALUE_DISCARDED", + "PROPERTY_USED_AS_FUNCTION", + "CONSTANT_USED_AS_FUNCTION", + "FUNCTION_USED_AS_PROPERTY", + "INTEGER_DIVISION", + "UNSAFE_PROPERTY_ACCESS", + "UNSAFE_METHOD_ACCESS", + "UNSAFE_CAST", + "UNSAFE_CALL_ARGUMENT", + NULL + }; + + return names[(int)p_code]; +} + +GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) { + for (int i = 0; i < WARNING_MAX; i++) { + if (get_name_from_code((Code)i) == p_name) { + return (Code)i; + } + } + + ERR_EXPLAIN("Invalid GDScript waring name: " + p_name); + ERR_FAIL_V(WARNING_MAX); +} + +#endif // DEBUG_ENABLED + GDScriptLanguage::GDScriptLanguage() { calls = 0; @@ -1903,6 +2066,15 @@ GDScriptLanguage::GDScriptLanguage() { _debug_max_call_stack = 0; _call_stack = NULL; } + +#ifdef DEBUG_ENABLED + GLOBAL_DEF("debug/gdscript/warnings/enable", true); + GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false); + for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { + String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); + GLOBAL_DEF("debug/gdscript/warnings/" + warning, !warning.begins_with("unsafe_")); + } +#endif // DEBUG_ENABLED } GDScriptLanguage::~GDScriptLanguage() { diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d5fe7a000b..edad12f1f3 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -261,6 +261,49 @@ public: ~GDScriptInstance(); }; +#ifdef DEBUG_ENABLED +struct GDScriptWarning { + enum Code { + UNASSIGNED_VARIABLE, // Variable used but never assigned + UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) + UNUSED_VARIABLE, // Local variable is declared but never used + UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file + UNUSED_ARGUMENT, // Function argument is never used + UNREACHABLE_CODE, // Code after a return statement + STANDALONE_EXPRESSION, // Expression not assigned to a variable + VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable + NARROWING_CONVERSION, // Float value into an integer slot, precision is lost + FUNCTION_MAY_YIELD, // Typed assign of function call that yields (it may return a function state) + VARIABLE_CONFLICTS_FUNCTION, // Variable has the same name of a function + FUNCTION_CONFLICTS_VARIABLE, // Function has the same name of a variable + FUNCTION_CONFLICTS_CONSTANT, // Function has the same name of a constant + INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible + UNUSED_SIGNAL, // Signal is defined but never emitted + RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used + PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name + CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name + FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name + INTEGER_DIVISION, // Integer divide by integer, decimal part is discarded + UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes) + UNSAFE_METHOD_ACCESS, // Fucntion not found in the detected type (but can be in subtypes) + UNSAFE_CAST, // Cast used in an unknown type + UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument + WARNING_MAX, + } code; + Vector<String> symbols; + int line; + + String get_name() const; + String get_message() const; + static String get_name_from_code(Code p_code); + static Code get_code_from_name(const String &p_name); + + GDScriptWarning() : + line(-1), + code(WARNING_MAX) {} +}; +#endif // DEBUG_ENABLED + class GDScriptLanguage : public ScriptLanguage { static GDScriptLanguage *singleton; @@ -355,10 +398,10 @@ public: Vector<StackInfo> csi; csi.resize(_debug_call_stack_pos); for (int i = 0; i < _debug_call_stack_pos; i++) { - csi[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0; + csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0; if (_call_stack[i].function) - csi[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name(); - csi[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); + csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name(); + csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path(); } return csi; } @@ -397,7 +440,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index a428ccd306..fe393957db 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -140,7 +140,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } break; case GDScriptParser::DataType::CLASS: { result.kind = GDScriptDataType::GDSCRIPT; - if (p_datatype.class_type->name == StringName()) { + if (!p_datatype.class_type->owner) { result.script_type = Ref<GDScript>(main_script); } else { result.script_type = class_map[p_datatype.class_type->name]; @@ -482,7 +482,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: Variant script; int idx = -1; - if (cn->cast_type.class_type->name == StringName()) { + if (!cn->cast_type.class_type->owner) { script = codegen.script; } else { StringName name = cn->cast_type.class_type->name; @@ -785,8 +785,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size() + 3); - codegen.opcodes[jump_fail_pos] = codegen.opcodes.size(); - codegen.opcodes[jump_fail_pos2] = codegen.opcodes.size(); + codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size(); + codegen.opcodes.write[jump_fail_pos2] = codegen.opcodes.size(); codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE); codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS; @@ -818,8 +818,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); codegen.opcodes.push_back(codegen.opcodes.size() + 3); - codegen.opcodes[jump_success_pos] = codegen.opcodes.size(); - codegen.opcodes[jump_success_pos2] = codegen.opcodes.size(); + codegen.opcodes.write[jump_success_pos] = codegen.opcodes.size(); + codegen.opcodes.write[jump_success_pos2] = codegen.opcodes.size(); codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE); codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS; @@ -850,7 +850,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: int jump_past_pos = codegen.opcodes.size(); codegen.opcodes.push_back(0); - codegen.opcodes[jump_fail_pos] = codegen.opcodes.size(); + codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size(); res = _parse_expression(codegen, on->arguments[2], p_stack_level); if (res < 0) return res; @@ -859,7 +859,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); codegen.opcodes.push_back(res); - codegen.opcodes[jump_past_pos] = codegen.opcodes.size(); + codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size(); return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS; @@ -1181,7 +1181,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: Variant script; int idx = -1; - if (assign_type.class_type->name == StringName()) { + if (!assign_type.class_type->owner) { script = codegen.script; } else { StringName name = assign_type.class_type->name; @@ -1361,10 +1361,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); codegen.opcodes.push_back(break_addr); - codegen.opcodes[continue_addr + 1] = codegen.opcodes.size(); + codegen.opcodes.write[continue_addr + 1] = codegen.opcodes.size(); } - codegen.opcodes[break_addr + 1] = codegen.opcodes.size(); + codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size(); } break; @@ -1393,16 +1393,16 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); int end_addr = codegen.opcodes.size(); codegen.opcodes.push_back(0); - codegen.opcodes[else_addr] = codegen.opcodes.size(); + codegen.opcodes.write[else_addr] = codegen.opcodes.size(); Error err = _parse_block(codegen, cf->body_else, p_stack_level, p_break_addr, p_continue_addr); if (err) return err; - codegen.opcodes[end_addr] = codegen.opcodes.size(); + codegen.opcodes.write[end_addr] = codegen.opcodes.size(); } else { //end without else - codegen.opcodes[else_addr] = codegen.opcodes.size(); + codegen.opcodes.write[else_addr] = codegen.opcodes.size(); } } break; @@ -1453,7 +1453,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_pos); - codegen.opcodes[break_pos + 1] = codegen.opcodes.size(); + codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size(); codegen.pop_stack_identifiers(); @@ -1479,7 +1479,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Blo codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); codegen.opcodes.push_back(continue_addr); - codegen.opcodes[break_addr + 1] = codegen.opcodes.size(); + codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size(); } break; case GDScriptParser::ControlFlowNode::CF_SWITCH: { @@ -1689,7 +1689,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser gdfunc->rpc_mode = p_func->rpc_mode; gdfunc->argument_types.resize(p_func->argument_types.size()); for (int i = 0; i < p_func->argument_types.size(); i++) { - gdfunc->argument_types[i] = _gdtype_from_datatype(p_func->argument_types[i]); + gdfunc->argument_types.write[i] = _gdtype_from_datatype(p_func->argument_types[i]); } gdfunc->return_type = _gdtype_from_datatype(p_func->return_type); } else { @@ -1708,11 +1708,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser if (codegen.constant_map.size()) { gdfunc->_constant_count = codegen.constant_map.size(); gdfunc->constants.resize(codegen.constant_map.size()); - gdfunc->_constants_ptr = &gdfunc->constants[0]; + gdfunc->_constants_ptr = gdfunc->constants.ptrw(); const Variant *K = NULL; while ((K = codegen.constant_map.next(K))) { int idx = codegen.constant_map[*K]; - gdfunc->constants[idx] = *K; + gdfunc->constants.write[idx] = *K; } } else { @@ -1726,7 +1726,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser gdfunc->_global_names_ptr = &gdfunc->global_names[0]; for (Map<StringName, int>::Element *E = codegen.name_map.front(); E; E = E->next()) { - gdfunc->global_names[E->get()] = E->key(); + gdfunc->global_names.write[E->get()] = E->key(); } gdfunc->_global_names_count = gdfunc->global_names.size(); @@ -1741,7 +1741,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser gdfunc->named_globals.resize(codegen.named_globals.size()); gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr(); for (int i = 0; i < codegen.named_globals.size(); i++) { - gdfunc->named_globals[i] = codegen.named_globals[i]; + gdfunc->named_globals.write[i] = codegen.named_globals[i]; } gdfunc->_named_globals_count = gdfunc->named_globals.size(); } @@ -1994,7 +1994,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner p_script->_signals[name] = p_class->_signals[i].arguments; } - if (p_class->name != StringName()) { + if (!p_class->owner) { parsed_classes.insert(p_class->name); } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 2e4a4c40dd..abd56d2757 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -116,11 +116,24 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p p_script->set_source_code(src); } -bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const { +bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { GDScriptParser parser; Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines); +#ifdef DEBUG_ENABLED + if (r_warnings) { + for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) { + const GDScriptWarning &warn = E->get(); + ScriptLanguage::Warning w; + w.line = warn.line; + w.code = (int)warn.code; + w.string_code = GDScriptWarning::get_name_from_code(warn.code); + w.message = warn.get_message(); + r_warnings->push_back(w); + } + } +#endif if (err) { r_line_error = parser.get_error_line(); r_col_error = parser.get_error_column(); @@ -2442,7 +2455,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } break; case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { List<StringName> constants; - Variant::get_numeric_constants_for_type(parser.get_completion_built_in_constant(), &constants); + Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants); for (List<StringName>::Element *E = constants.front(); E; E = E->next()) { options.insert(E->get().operator String()); } @@ -2918,7 +2931,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t } //print_line(itos(indent_stack.size())+","+itos(tc)+": "+l); - lines[i] = l; + lines.write[i] = l; } p_code = ""; @@ -3065,7 +3078,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co case GDScriptParser::DataType::BUILTIN: { base_type.has_type = false; - if (Variant::has_numeric_constant(base_type.builtin_type, p_symbol)) { + if (Variant::has_constant(base_type.builtin_type, p_symbol)) { r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; r_result.class_name = Variant::get_type_name(base_type.builtin_type); r_result.class_member = p_symbol; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index 6a08d86904..bae3f48923 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -62,7 +62,7 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta } #endif //member indexing is O(1) - return &p_instance->members[address]; + return &p_instance->members.write[address]; } break; case ADDR_TYPE_CLASS_CONSTANT: { @@ -742,13 +742,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX); +#ifdef DEBUG_ENABLED if (src->get_type() != var_type) { - err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + Variant::get_type_name(var_type) + "'."; - OPCODE_BREAK; + if (Variant::can_convert_strict(src->get_type(), var_type)) { + Variant::CallError ce; + *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce); + } else { + err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + + "' to a variable of type '" + Variant::get_type_name(var_type) + "'."; + OPCODE_BREAK; + } + } else { +#endif // DEBUG_ENABLED + *dst = *src; +#ifdef DEBUG_ENABLED } - - *dst = *src; +#endif // DEBUG_ENABLED ip += 4; } @@ -761,17 +770,22 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); +#ifdef DEBUG_ENABLED GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *()); GD_ERR_BREAK(!nc); + if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) { + err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + + "' to a variable of type '" + nc->get_name() + "'."; + OPCODE_BREAK; + } Object *src_obj = src->operator Object *(); - GD_ERR_BREAK(!src_obj); - if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) { + if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) { err_text = "Trying to assign value of type '" + src_obj->get_class_name() + "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; } - +#endif // DEBUG_ENABLED *dst = *src; ip += 4; @@ -785,6 +799,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_VARIANT_PTR(dst, 2); GET_VARIANT_PTR(src, 3); +#ifdef DEBUG_ENABLED Script *base_type = Object::cast_to<Script>(type->operator Object *()); GD_ERR_BREAK(!base_type); @@ -820,6 +835,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } } +#endif // DEBUG_ENABLED *dst = *src; @@ -1218,7 +1234,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a gdfs->state.stack.resize(alloca_size); //copy variant stack for (int i = 0; i < _stack_size; i++) { - memnew_placement(&gdfs->state.stack[sizeof(Variant) * i], Variant(stack[i])); + memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i])); } gdfs->state.stack_size = _stack_size; gdfs->state.self = self; @@ -1579,7 +1595,7 @@ StringName GDScriptFunction::get_global_name(int p_idx) const { int GDScriptFunction::get_default_argument_count() const { - return default_arguments.size(); + return _default_arg_count; } int GDScriptFunction::get_default_argument_addr(int p_idx) const { diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp index 7e98b6ced9..f2e52d48dd 100644 --- a/modules/gdscript/gdscript_functions.cpp +++ b/modules/gdscript/gdscript_functions.cpp @@ -1098,7 +1098,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_ for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { if (d.has(E->key())) { - ins->members[E->get().index] = d[E->key()]; + ins->members.write[E->get().index] = d[E->key()]; } } @@ -1412,7 +1412,7 @@ bool GDScriptFunctions::is_deterministic(Function p_func) { MethodInfo GDScriptFunctions::get_info(Function p_func) { -#ifdef TOOLS_ENABLED +#ifdef DEBUG_ENABLED //using a switch, so the compiler generates a jumptable switch (p_func) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index ac53f33e9e..e0ed2b332b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -38,6 +38,7 @@ #include "io/resource_loader.h" #include "os/file_access.h" #include "print_string.h" +#include "project_settings.h" #include "script_language.h" template <class T> @@ -56,6 +57,8 @@ T *GDScriptParser::alloc_node() { return t; } +static String _find_function_name(const GDScriptParser::OperatorNode *p_call); + bool GDScriptParser::_end_statement() { if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) { @@ -607,7 +610,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s _set_error("Built-in type constant or static function expected after '.'"); return NULL; } - if (!Variant::has_numeric_constant(bi_type, identifier)) { + if (!Variant::has_constant(bi_type, identifier)) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN && Variant::is_method_const(bi_type, identifier) && @@ -642,7 +645,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } else { ConstantNode *cn = alloc_node<ConstantNode>(); - cn->value = Variant::get_numeric_constant_value(bi_type, identifier); + cn->value = Variant::get_constant_value(bi_type, identifier); cn->datatype = _type_from_variant(cn->value); expr = cn; } @@ -726,7 +729,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } BlockNode *b = current_block; - while (b) { + while (!bfn && b) { if (b->variables.has(identifier)) { IdentifierNode *id = alloc_node<IdentifierNode>(); LocalVarNode *lv = b->variables[identifier]; @@ -736,6 +739,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s expr = id; bfn = true; +#ifdef DEBUG_ENABLED switch (tokenizer->get_token()) { case GDScriptTokenizer::TK_OP_ASSIGN_ADD: case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: @@ -747,15 +751,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: case GDScriptTokenizer::TK_OP_ASSIGN_SUB: { - if (lv->assignments == 0 && !lv->datatype.has_type) { - _set_error("Using assignment with operation on a variable that was never assigned."); - return NULL; + if (lv->assignments == 0) { + if (!lv->datatype.has_type) { + _set_error("Using assignment with operation on a variable that was never assigned."); + return NULL; + } + _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, -1, identifier.operator String()); } } // fallthrough case GDScriptTokenizer::TK_OP_ASSIGN: { lv->assignments += 1; + lv->usages--; // Assignment is not really usage + } break; + default: { + lv->usages++; } } +#endif // DEBUG_ENABLED break; } b = b->parent_block; @@ -785,6 +797,32 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } if (!bfn) { +#ifdef DEBUG_ENABLED + if (current_function) { + int arg_idx = current_function->arguments.find(identifier); + if (arg_idx != -1) { + switch (tokenizer->get_token()) { + case GDScriptTokenizer::TK_OP_ASSIGN_ADD: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR: + case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR: + case GDScriptTokenizer::TK_OP_ASSIGN_DIV: + case GDScriptTokenizer::TK_OP_ASSIGN_MOD: + case GDScriptTokenizer::TK_OP_ASSIGN_MUL: + case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: + case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: + case GDScriptTokenizer::TK_OP_ASSIGN_SUB: + case GDScriptTokenizer::TK_OP_ASSIGN: { + // Assignment is not really usage + current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1; + } break; + default: { + current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1; + } + } + } + } +#endif // DEBUG_ENABLED IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = identifier; id->line = id_line; @@ -1411,8 +1449,8 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s op->op = expression[i].op; op->arguments.push_back(expression[i + 1].node); op->line = op_line; //line might have been changed from a \n - expression[i].is_op = false; - expression[i].node = op; + expression.write[i].is_op = false; + expression.write[i].node = op; expression.remove(i + 1); } @@ -1466,7 +1504,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s op->arguments.push_back(expression[next_op + 3].node); //expression after next goes as when-false //replace all 3 nodes by this operator and make it an expression - expression[next_op - 1].node = op; + expression.write[next_op - 1].node = op; expression.remove(next_op); expression.remove(next_op); expression.remove(next_op); @@ -1502,7 +1540,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s op->arguments.push_back(expression[next_op + 1].node); //next expression goes as right //replace all 3 nodes by this operator and make it an expression - expression[next_op - 1].node = op; + expression.write[next_op - 1].node = op; expression.remove(next_op); expression.remove(next_op); } @@ -1526,7 +1564,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to for (int i = 0; i < an->elements.size(); i++) { - an->elements[i] = _reduce_expression(an->elements[i], p_to_const); + an->elements.write[i] = _reduce_expression(an->elements[i], p_to_const); if (an->elements[i]->type != Node::TYPE_CONSTANT) all_constants = false; } @@ -1556,10 +1594,10 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to for (int i = 0; i < dn->elements.size(); i++) { - dn->elements[i].key = _reduce_expression(dn->elements[i].key, p_to_const); + dn->elements.write[i].key = _reduce_expression(dn->elements[i].key, p_to_const); if (dn->elements[i].key->type != Node::TYPE_CONSTANT) all_constants = false; - dn->elements[i].value = _reduce_expression(dn->elements[i].value, p_to_const); + dn->elements.write[i].value = _reduce_expression(dn->elements[i].value, p_to_const); if (dn->elements[i].value->type != Node::TYPE_CONSTANT) all_constants = false; } @@ -1592,7 +1630,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to for (int i = 0; i < op->arguments.size(); i++) { - op->arguments[i] = _reduce_expression(op->arguments[i], p_to_const); + op->arguments.write[i] = _reduce_expression(op->arguments[i], p_to_const); if (op->arguments[i]->type != Node::TYPE_CONSTANT) { all_constants = false; last_not_constant = i; @@ -1620,7 +1658,7 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to for (int i = 0; i < ptrs.size(); i++) { ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i + 1]); - ptrs[i] = &cn->value; + ptrs.write[i] = &cn->value; } vptr = (const Variant **)&ptrs[0]; @@ -2601,6 +2639,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { pending_newline = -1; } +#ifdef DEBUG_ENABLED switch (token) { case GDScriptTokenizer::TK_EOF: case GDScriptTokenizer::TK_ERROR: @@ -2609,13 +2648,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { // will check later } break; default: { - // TODO: Make this a warning - /*if (p_block->has_return) { - _set_error("Unreacheable code."); - return; - }*/ + if (p_block->has_return && !current_function->has_unreachable_code) { + _add_warning(GDScriptWarning::UNREACHABLE_CODE, -1, current_function->name.operator String()); + current_function->has_unreachable_code = true; + } } break; } +#endif // DEBUG_ENABLED switch (token) { case GDScriptTokenizer::TK_EOF: p_block->end_line = tokenizer->get_token_line(); @@ -2728,6 +2767,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { c->line = var_line; assigned = c; } + lv->assign = assigned; //must be added later, to avoid self-referencing. p_block->variables.insert(n, lv); @@ -2745,6 +2785,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { lv->assign_op = op; lv->assign = assigned; + lv->assign_op = op; + if (!_end_statement()) { _set_error("Expected end of statement (var)"); return; @@ -3332,6 +3374,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { switch (token) { + case GDScriptTokenizer::TK_CURSOR: { + tokenizer->advance(); + } break; case GDScriptTokenizer::TK_EOF: p_class->end_line = tokenizer->get_token_line(); case GDScriptTokenizer::TK_ERROR: { @@ -3510,6 +3555,17 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } +#ifdef DEBUG_ENABLED + if (p_class->constant_expressions.has(name)) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + for (int i = 0; i < p_class->variables.size(); i++) { + if (p_class->variables[i].identifier == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); + } + } +#endif // DEBUG_ENABLED + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' )."); @@ -3521,6 +3577,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { Vector<StringName> arguments; Vector<DataType> argument_types; Vector<Node *> default_values; +#ifdef DEBUG_ENABLED + Vector<int> arguments_usage; +#endif // DEBUG_ENABLED int fnline = tokenizer->get_token_line(); @@ -3547,12 +3606,18 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { StringName argname = tokenizer->get_token_identifier(); arguments.push_back(argname); +#ifdef DEBUG_ENABLED + arguments_usage.push_back(0); +#endif // DEBUG_ENABLED tokenizer->advance(); DataType argtype; if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { - if (!_parse_type(argtype)) { + if (tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) { + argtype.infer_type = true; + tokenizer->advance(); + } else if (!_parse_type(argtype)) { _set_error("Expected type for argument."); return; } @@ -3697,7 +3762,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { function->default_values = default_values; function->_static = _static; function->line = fnline; - +#ifdef DEBUG_ENABLED + function->arguments_usage = arguments_usage; +#endif // DEBUG_ENABLED function->rpc_mode = rpc_mode; rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; @@ -3724,6 +3791,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { ClassNode::Signal sig; sig.name = tokenizer->get_token_identifier(); + sig.emissions = 0; + sig.line = tokenizer->get_token_line(); tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -4187,6 +4256,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; current_export.hint_string = native_class->get_name(); + current_export.class_name = native_class->get_name(); } else { current_export = PropertyInfo(); @@ -4406,6 +4476,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.expression = NULL; member._export.name = member.identifier; member.line = tokenizer->get_token_line(); + member.usages = 0; member.rpc_mode = rpc_mode; if (current_class->constant_expressions.has(member.identifier)) { @@ -4421,7 +4492,20 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } - +#ifdef DEBUG_ENABLED + for (int i = 0; i < current_class->functions.size(); i++) { + if (current_class->functions[i]->name == member.identifier) { + _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier); + break; + } + } + for (int i = 0; i < current_class->static_functions.size(); i++) { + if (current_class->static_functions[i]->name == member.identifier) { + _add_warning(GDScriptWarning::VARIABLE_CONFLICTS_FUNCTION, member.line, member.identifier); + break; + } + } +#endif // DEBUG_ENABLED tokenizer->advance(); rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; @@ -4546,6 +4630,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member._export.type = Variant::OBJECT; member._export.hint = PROPERTY_HINT_RESOURCE_TYPE; member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + member._export.hint_string = member.data_type.native_type; member._export.class_name = member.data_type.native_type; } else { _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); @@ -5433,6 +5518,9 @@ GDScriptParser::DataType GDScriptParser::_get_operation_type(const Variant::Oper if (b_type == Variant::INT || b_type == Variant::REAL) { Variant::evaluate(Variant::OP_ADD, b, 1, b, r_valid); } + if (a_type == Variant::STRING && b_type != Variant::ARRAY) { + a = "%s"; // Work around for formatting operator (%) + } Variant ret; Variant::evaluate(p_op, a, b, ret, r_valid); @@ -5678,11 +5766,26 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { node_type.has_type = true; node_type.kind = DataType::BUILTIN; node_type.builtin_type = Variant::ARRAY; +#ifdef DEBUG_ENABLED + // Check stuff inside the array + ArrayNode *an = static_cast<ArrayNode *>(p_node); + for (int i = 0; i < an->elements.size(); i++) { + _reduce_node_type(an->elements[i]); + } +#endif // DEBUG_ENABLED } break; case Node::TYPE_DICTIONARY: { node_type.has_type = true; node_type.kind = DataType::BUILTIN; node_type.builtin_type = Variant::DICTIONARY; +#ifdef DEBUG_ENABLED + // Check stuff inside the dictionarty + DictionaryNode *dn = static_cast<DictionaryNode *>(p_node); + for (int i = 0; i < dn->elements.size(); i++) { + _reduce_node_type(dn->elements[i].key); + _reduce_node_type(dn->elements[i].value); + } +#endif // DEBUG_ENABLED } break; case Node::TYPE_SELF: { node_type.has_type = true; @@ -5693,6 +5796,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { IdentifierNode *id = static_cast<IdentifierNode *>(p_node); if (id->declared_block) { node_type = id->declared_block->variables[id->name]->get_datatype(); + id->declared_block->variables[id->name]->usages += 1; } else if (id->name == "#match_value") { // It's a special id just for the match statetement, ignore break; @@ -5727,6 +5831,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } else { +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::UNSAFE_CAST, cn->line, cn->cast_type.to_string()); +#endif // DEBUG_ENABLED _mark_line_as_unsafe(cn->line); } @@ -5853,6 +5960,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { op->line, op->column); return DataType(); } +#ifdef DEBUG_ENABLED + if (var_op == Variant::OP_DIVIDE && argument_a_type.has_type && argument_a_type.kind == DataType::BUILTIN && argument_a_type.builtin_type == Variant::INT && + argument_b_type.has_type && argument_b_type.kind == DataType::BUILTIN && argument_b_type.builtin_type == Variant::INT) { + _add_warning(GDScriptWarning::INTEGER_DIVISION, op->line); + } +#endif // DEBUG_ENABLED } break; // Ternary operators @@ -5871,10 +5984,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { node_type = true_type; } else if (_is_type_compatible(false_type, true_type)) { node_type = false_type; + } else { +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::INCOMPATIBLE_TERNARY, op->line); +#endif // DEBUG_ENABLED } - - // TODO: Warn if types aren't compatible - } break; // Assignment should never happen within an expression case OperatorNode::OP_ASSIGN: @@ -5937,6 +6051,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { node_type = result; } else { node_type = _reduce_identifier_type(&base_type, member_id->name, op->line); +#ifdef DEBUG_ENABLED + if (!node_type.has_type) { + _add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string()); + } +#endif // DEBUG_ENABLED } } else { _mark_line_as_unsafe(op->line); @@ -5956,7 +6075,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { id->name = cn->value.operator StringName(); op->op = OperatorNode::OP_INDEX_NAMED; - op->arguments[1] = id; + op->arguments.write[1] = id; return _reduce_node_type(op); } @@ -6323,7 +6442,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat Vector<DataType> par_types; par_types.resize(p_call->arguments.size() - 1); for (int i = 1; i < p_call->arguments.size(); i++) { - par_types[i - 1] = _reduce_node_type(p_call->arguments[i]); + par_types.write[i - 1] = _reduce_node_type(p_call->arguments[i]); } if (error_set) return DataType(); @@ -6356,6 +6475,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (!_is_type_compatible(arg_type, par_types[i], true)) { types_match = false; break; + } else { +#ifdef DEBUG_ENABLED + if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_types[i].kind == DataType::BUILTIN && par_types[i].builtin_type == Variant::REAL) { + _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, Variant::get_type_name(tn->vtype)); + } + if (par_types[i].may_yield && p_call->arguments[i + 1]->type == Node::TYPE_OPERATOR) { + _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i + 1]))); + } +#endif // DEBUG_ENABLED } } @@ -6389,6 +6517,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat return_type = _type_from_property(mi.return_val, false); +#ifdef DEBUG_ENABLED + // Check all arguments beforehand to solve warnings + for (int i = 1; i < p_call->arguments.size(); i++) { + _reduce_node_type(p_call->arguments[i]); + } +#endif // DEBUG_ENABLED + // Check arguments is_vararg = mi.flags & METHOD_FLAG_VARARG; @@ -6415,6 +6550,13 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat ERR_FAIL_V(DataType()); } +#ifdef DEBUG_ENABLED + // Check all arguments beforehand to solve warnings + for (int i = arg_id + 1; i < p_call->arguments.size(); i++) { + _reduce_node_type(p_call->arguments[i]); + } +#endif // DEBUG_ENABLED + IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]); callee_name = func_id->name; arg_count -= 1 + arg_id; @@ -6494,8 +6636,18 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat _set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line); return DataType(); } + DataType tmp_type; + valid = _get_member_type(original_type, func_id->name, tmp_type); + if (valid) { + if (tmp_type.is_constant) { + _add_warning(GDScriptWarning::CONSTANT_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string()); + } else { + _add_warning(GDScriptWarning::PROPERTY_USED_AS_FUNCTION, p_call->line, callee_name, original_type.to_string()); + } + } + _add_warning(GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->line, callee_name, original_type.to_string()); _mark_line_as_unsafe(p_call->line); -#endif +#endif // DEBUG_ENABLED return DataType(); } @@ -6511,7 +6663,19 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat _set_error("Non-static function '" + String(callee_name) + "' can only be called from an instance.", p_call->line); return DataType(); } -#endif + + // Check signal emission for warnings + if (callee_name == "emit_signal" && p_call->op == OperatorNode::OP_CALL && p_call->arguments[0]->type == Node::TYPE_SELF && p_call->arguments.size() >= 3 && p_call->arguments[2]->type == Node::TYPE_CONSTANT) { + ConstantNode *sig = static_cast<ConstantNode *>(p_call->arguments[2]); + String emitted = sig->value.get_type() == Variant::STRING ? sig->value.operator String() : ""; + for (int i = 0; i < current_class->_signals.size(); i++) { + if (current_class->_signals[i].name == emitted) { + current_class->_signals.write[i].emissions += 1; + break; + } + } + } +#endif // DEBUG_ENABLED } break; } @@ -6536,8 +6700,15 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat continue; } + DataType arg_type = arg_types[i - arg_diff]; + if (!par_type.has_type) { _mark_line_as_unsafe(p_call->line); +#ifdef DEBUG_ENABLED + if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) { + _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i]))); + } +#endif // DEBUG_ENABLED } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { // Supertypes are acceptable for dynamic compliance if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) { @@ -6549,6 +6720,12 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } else { _mark_line_as_unsafe(p_call->line); } + } else { +#ifdef DEBUG_ENABLED + if (arg_type.kind == DataType::BUILTIN && arg_type.builtin_type == Variant::INT && par_type.kind == DataType::BUILTIN && par_type.builtin_type == Variant::REAL) { + _add_warning(GDScriptWarning::NARROWING_CONVERSION, p_call->line, callee_name); + } +#endif // DEBUG_ENABLED } } @@ -6770,7 +6947,35 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType // Check classes in current file ClassNode *base = NULL; if (!p_base_type) { - // Possibly this is a global, check first + base = current_class; + base_type.has_type = true; + base_type.is_constant = true; + base_type.kind = DataType::CLASS; + base_type.class_type = base; + } else { + base_type = DataType(*p_base_type); + if (base_type.kind == DataType::CLASS) { + base = base_type.class_type; + } + } + + DataType member_type; + + for (int i = 0; i < current_class->variables.size(); i++) { + ClassNode::Member m = current_class->variables[i]; + if (current_class->variables[i].identifier == p_identifier) { + member_type = current_class->variables[i].data_type; + current_class->variables.write[i].usages += 1; + return member_type; + } + } + + if (_get_member_type(base_type, p_identifier, member_type)) { + return member_type; + } + + if (!p_base_type) { + // Possibly this is a global, check before failing if (ClassDB::class_exists(p_identifier) || ClassDB::class_exists("_" + p_identifier.operator String())) { DataType result; @@ -6796,6 +7001,9 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType result.class_type = outer_class; return result; } + if (outer_class->constant_expressions.has(p_identifier)) { + return outer_class->constant_expressions[p_identifier].type; + } for (int i = 0; i < outer_class->subclasses.size(); i++) { if (outer_class->subclasses[i] == current_class) { continue; @@ -6885,31 +7093,23 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType } } - // Nothing found, keep looking in local scope - - base = current_class; - base_type.has_type = true; - base_type.is_constant = true; - base_type.kind = DataType::CLASS; - base_type.class_type = base; - } else { - base_type = *p_base_type; - if (base_type.kind == DataType::CLASS) { - base = base_type.class_type; - } - } - - DataType member_type; - - if (_get_member_type(base_type, p_identifier, member_type)) { - return member_type; - } - - if (!p_base_type) { // This means looking in the current class, which type is always known _set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line); } +#ifdef DEBUG_ENABLED + { + DataType tmp_type; + List<DataType> arg_types; + int argcount; + bool _static; + bool vararg; + if (_get_function_signature(base_type, p_identifier, tmp_type, arg_types, argcount, _static, vararg)) { + _add_warning(GDScriptWarning::FUNCTION_USED_AS_PROPERTY, p_line, p_identifier.operator String(), base_type.to_string()); + } + } +#endif // DEBUG_ENABLED + _mark_line_as_unsafe(p_line); return DataType(); } @@ -6949,7 +7149,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { // Class variables for (int i = 0; i < p_class->variables.size(); i++) { - ClassNode::Member &v = p_class->variables[i]; + ClassNode::Member &v = p_class->variables.write[i]; DataType tmp; if (_get_member_type(p_class->base_type, v.identifier, tmp)) { @@ -6993,7 +7193,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { convert_call->arguments.push_back(tgt_type); v.expression = convert_call; - v.initial_assignment->arguments[1] = convert_call; + v.initial_assignment->arguments.write[1] = convert_call; } } @@ -7131,11 +7331,9 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { // Arguments int defaults_ofs = p_function->arguments.size() - p_function->default_values.size(); for (int i = 0; i < p_function->arguments.size(); i++) { - - // Resolve types - p_function->argument_types[i] = _resolve_type(p_function->argument_types[i], p_function->line); - - if (i >= defaults_ofs) { + if (i < defaults_ofs) { + p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line); + } else { if (p_function->default_values[i - defaults_ofs]->type != Node::TYPE_OPERATOR) { _set_error("Parser bug: invalid argument default value.", p_function->line, p_function->column); return; @@ -7150,17 +7348,30 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { DataType def_type = _reduce_node_type(op->arguments[1]); - if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) { - String arg_name = p_function->arguments[i]; - _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" + - arg_name + "' (" + p_function->arguments[i] + ")", - p_function->line); + if (p_function->argument_types[i].infer_type) { + def_type.is_constant = false; + p_function->argument_types.write[i] = def_type; + } else { + p_function->return_type = _resolve_type(p_function->return_type, p_function->line); + + if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) { + String arg_name = p_function->arguments[i]; + _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" + + arg_name + "' (" + p_function->arguments[i] + ")", + p_function->line); + } } } +#ifdef DEBUG_ENABLED + if (p_function->arguments_usage[i] == 0) { + _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); + } +#endif // DEBUG_ENABLED } if (!(p_function->name == "_init")) { // Signature for the initializer may vary +#ifdef DEBUG_ENABLED DataType return_type; List<DataType> arg_types; int default_arg_count = 0; @@ -7171,18 +7382,44 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (_get_function_signature(base_type, p_function->name, return_type, arg_types, default_arg_count, _static, vararg)) { bool valid = _static == p_function->_static; valid = valid && return_type == p_function->return_type; - valid = valid && p_function->default_values.size() >= default_arg_count; - valid = valid && arg_types.size() == p_function->arguments.size(); + int argsize_diff = p_function->arguments.size() - arg_types.size(); + valid = valid && argsize_diff >= 0; + valid = valid && p_function->default_values.size() >= default_arg_count + argsize_diff; int i = 0; for (List<DataType>::Element *E = arg_types.front(); valid && E; E = E->next()) { valid = valid && E->get() == p_function->argument_types[i++]; } if (!valid) { - _set_error("Function signature doesn't match the parent.", p_function->line); + String parent_signature = return_type.has_type ? return_type.to_string() : "Variant"; + if (parent_signature == "null") { + parent_signature = "void"; + } + parent_signature += " " + p_function->name + "("; + if (arg_types.size()) { + int i = 0; + for (List<DataType>::Element *E = arg_types.front(); E; E = E->next()) { + if (E != arg_types.front()) { + parent_signature += ", "; + } + String arg = E->get().to_string(); + if (arg == "null" || arg == "var") { + arg = "Variant"; + } + parent_signature += arg; + if (i == arg_types.size() - default_arg_count) { + parent_signature += "=default"; + } + + i++; + } + } + parent_signature += ")"; + _set_error("Function signature doesn't match the parent. Parent signature is: '" + parent_signature + "'.", p_function->line); return; } } +#endif // DEBUG_ENABLED } else { if (p_function->return_type.has_type && (p_function->return_type.kind != DataType::BUILTIN || p_function->return_type.builtin_type != Variant::NIL)) { _set_error("Constructor cannot return a value.", p_function->line); @@ -7200,6 +7437,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->has_yield) { // yield() will make the function return a GDScriptFunctionState, so the type is ambiguous p_function->return_type.has_type = false; + p_function->return_type.may_yield = true; } } @@ -7226,6 +7464,20 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { if (error_set) return; } +#ifdef DEBUG_ENABLED + // Warnings + for (int i = 0; i < p_class->variables.size(); i++) { + if (p_class->variables[i].usages == 0) { + _add_warning(GDScriptWarning::UNUSED_CLASS_VARIABLE, p_class->variables[i].line, p_class->variables[i].identifier); + } + } + for (int i = 0; i < p_class->_signals.size(); i++) { + if (p_class->_signals[i].emissions == 0) { + _add_warning(GDScriptWarning::UNUSED_SIGNAL, p_class->_signals[i].line, p_class->_signals[i].name); + } + } +#endif // DEBUG_ENABLED + // Inner classes for (int i = 0; i < p_class->subclasses.size(); i++) { current_class = p_class->subclasses[i]; @@ -7235,6 +7487,26 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { } } +#ifdef DEBUG_ENABLED +static String _find_function_name(const GDScriptParser::OperatorNode *p_call) { + switch (p_call->arguments[0]->type) { + case GDScriptParser::Node::TYPE_TYPE: { + return Variant::get_type_name(static_cast<GDScriptParser::TypeNode *>(p_call->arguments[0])->vtype); + } break; + case GDScriptParser::Node::TYPE_BUILT_IN_FUNCTION: { + return GDScriptFunctions::get_func_name(static_cast<GDScriptParser::BuiltInFunctionNode *>(p_call->arguments[0])->function); + } break; + default: { + int id_index = p_call->op == GDScriptParser::OperatorNode::OP_PARENT_CALL ? 0 : 1; + if (p_call->arguments.size() > id_index && p_call->arguments[id_index]->type == GDScriptParser::Node::TYPE_IDENTIFIER) { + return static_cast<GDScriptParser::IdentifierNode *>(p_call->arguments[id_index])->name; + } + } break; + } + return String(); +} +#endif // DEBUG_ENABLED + void GDScriptParser::_check_block_types(BlockNode *p_block) { Node *last_var_assign = NULL; @@ -7253,8 +7525,23 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { lv->datatype = _resolve_type(lv->datatype, lv->line); _mark_line_as_safe(lv->line); + last_var_assign = lv->assign; if (lv->assign) { DataType assign_type = _reduce_node_type(lv->assign); +#ifdef DEBUG_ENABLED + if (assign_type.has_type && assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) { + if (lv->assign->type == Node::TYPE_OPERATOR) { + OperatorNode *call = static_cast<OperatorNode *>(lv->assign); + if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) { + _add_warning(GDScriptWarning::VOID_ASSIGNMENT, lv->line, _find_function_name(call)); + } + } + } + if (lv->datatype.has_type && assign_type.may_yield && lv->assign->type == Node::TYPE_OPERATOR) { + _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, lv->line, _find_function_name(static_cast<OperatorNode *>(lv->assign))); + } +#endif // DEBUG_ENABLED + if (!_is_type_compatible(lv->datatype, assign_type)) { // Try supertype test if (_is_type_compatible(assign_type, lv->datatype)) { @@ -7284,7 +7571,12 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { convert_call->arguments.push_back(tgt_type); lv->assign = convert_call; - lv->assign_op->arguments[1] = convert_call; + lv->assign_op->arguments.write[1] = convert_call; +#ifdef DEBUG_ENABLED + if (lv->datatype.builtin_type == Variant::INT && assign_type.builtin_type == Variant::REAL) { + _add_warning(GDScriptWarning::NARROWING_CONVERSION, lv->line); + } +#endif // DEBUG_ENABLED } } if (lv->datatype.infer_type) { @@ -7299,15 +7591,6 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { _mark_line_as_unsafe(lv->line); } } - last_var_assign = lv->assign; - - // TODO: Make a warning - /* - if (lv->assignments == 0) { - _set_error("Variable '" + String(lv->name) + "' is never assigned.", lv->line); - return; - } - */ } break; case Node::TYPE_OPERATOR: { OperatorNode *op = static_cast<OperatorNode *>(statement); @@ -7373,6 +7656,19 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } else { rh_type = _reduce_node_type(op->arguments[1]); } +#ifdef DEBUG_ENABLED + if (rh_type.has_type && rh_type.kind == DataType::BUILTIN && rh_type.builtin_type == Variant::NIL) { + if (op->arguments[1]->type == Node::TYPE_OPERATOR) { + OperatorNode *call = static_cast<OperatorNode *>(op->arguments[1]); + if (call->op == OperatorNode::OP_CALL || call->op == OperatorNode::OP_PARENT_CALL) { + _add_warning(GDScriptWarning::VOID_ASSIGNMENT, op->line, _find_function_name(call)); + } + } + } + if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) { + _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1]))); + } +#endif // DEBUG_ENABLED if (!_is_type_compatible(lh_type, rh_type)) { // Try supertype test @@ -7402,7 +7698,12 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { convert_call->arguments.push_back(op->arguments[1]); convert_call->arguments.push_back(tgt_type); - op->arguments[1] = convert_call; + op->arguments.write[1] = convert_call; +#ifdef DEBUG_ENABLED + if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) { + _add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line); + } +#endif // DEBUG_ENABLED } } if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) { @@ -7412,15 +7713,29 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { _mark_line_as_safe(op->line); - _reduce_function_call_type(op); + DataType func_type = _reduce_function_call_type(op); +#ifdef DEBUG_ENABLED + if (func_type.has_type && (func_type.kind != DataType::BUILTIN || func_type.builtin_type != Variant::NIL)) { + // Figure out function name for warning + String func_name = _find_function_name(op); + if (func_name.empty()) { + func_name == "<undetected name>"; + } + _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name); + } +#endif // DEBUG_ENABLED if (error_set) return; } break; + case OperatorNode::OP_YIELD: { + _mark_line_as_safe(op->line); + _reduce_node_type(op); + } break; default: { _mark_line_as_safe(op->line); _reduce_node_type(op); // Test for safety anyway - // TODO: Make this a warning - /*_set_error("Standalone expression, nothing is done in this line.", statement->line); - return; */ +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line); +#endif // DEBUG_ENABLED } } } break; @@ -7487,9 +7802,9 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { default: { _mark_line_as_safe(statement->line); _reduce_node_type(statement); // Test for safety anyway - // TODO: Make this a warning - /* _set_error("Standalone expression, nothing is done in this line.", statement->line); - return; */ +#ifdef DEBUG_ENABLED + _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line); +#endif // DEBUG_ENABLED } } } @@ -7501,6 +7816,18 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { current_block = p_block; if (error_set) return; } + +#ifdef DEBUG_ENABLED + // Warnings check + for (Map<StringName, LocalVarNode *>::Element *E = p_block->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + if (lv->usages == 0) { + _add_warning(GDScriptWarning::UNUSED_VARIABLE, lv->line, lv->name); + } else if (lv->assignments == 0) { + _add_warning(GDScriptWarning::UNASSIGNED_VARIABLE, lv->line, lv->name); + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) { @@ -7514,6 +7841,56 @@ void GDScriptParser::_set_error(const String &p_error, int p_line, int p_column) error_set = true; } +#ifdef DEBUG_ENABLED +void GDScriptParser::_add_warning(int p_code, int p_line, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) { + Vector<String> symbols; + if (!p_symbol1.empty()) { + symbols.push_back(p_symbol1); + } + if (!p_symbol2.empty()) { + symbols.push_back(p_symbol2); + } + if (!p_symbol3.empty()) { + symbols.push_back(p_symbol3); + } + if (!p_symbol4.empty()) { + symbols.push_back(p_symbol4); + } + _add_warning(p_code, p_line, symbols); +} + +void GDScriptParser::_add_warning(int p_code, int p_line, const Vector<String> &p_symbols) { + if (tokenizer->is_ignoring_warnings() || !GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) { + return; + } + String warn_name = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)p_code).to_lower(); + if (tokenizer->get_warning_global_skips().has(warn_name)) { + return; + } + if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) { + return; + } + + GDScriptWarning warn; + warn.code = (GDScriptWarning::Code)p_code; + warn.symbols = p_symbols; + warn.line = p_line == -1 ? tokenizer->get_token_line() : p_line; + + List<GDScriptWarning>::Element *before = NULL; + for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) { + if (E->get().line > warn.line) { + break; + } + before = E; + } + if (before) { + warnings.insert_after(before, warn); + } else { + warnings.push_front(warn); + } +} +#endif // DEBUG_ENABLED + String GDScriptParser::get_error() const { return error; @@ -7580,6 +7957,37 @@ Error GDScriptParser::_parse(const String &p_base_path) { return ERR_PARSE_ERROR; } +#ifdef DEBUG_ENABLED + // Resolve warning ignores + Vector<Pair<int, String> > warning_skips = tokenizer->get_warning_skips(); + bool warning_is_error = GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors").booleanize(); + for (List<GDScriptWarning>::Element *E = warnings.front(); E;) { + GDScriptWarning &w = E->get(); + int skip_index = -1; + for (int i = 0; i < warning_skips.size(); i++) { + if (warning_skips[i].first >= w.line) { + break; + } + skip_index = i; + } + List<GDScriptWarning>::Element *next = E->next(); + bool erase = false; + if (skip_index != -1) { + if (warning_skips[skip_index].second == GDScriptWarning::get_name_from_code(w.code).to_lower()) { + erase = true; + } + warning_skips.remove(skip_index); + } + if (erase) { + warnings.erase(E); + } else if (warning_is_error) { + _set_error(w.get_message() + " (warning treated as error)", w.line); + return ERR_PARSE_ERROR; + } + E = next; + } +#endif // DEBUG_ENABLED + return OK; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 48f256b4c6..d8ee4e8159 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -38,6 +38,7 @@ #include "script_language.h" struct GDScriptDataType; +struct GDScriptWarning; class GDScriptParser { public: @@ -57,6 +58,7 @@ public: bool is_constant; bool is_meta_type; // Whether the value can be used as a type bool infer_type; + bool may_yield; // For function calls Variant::Type builtin_type; StringName native_type; @@ -95,6 +97,7 @@ public: is_constant(false), is_meta_type(false), infer_type(false), + may_yield(false), builtin_type(Variant::NIL), class_type(NULL) {} }; @@ -160,6 +163,7 @@ public: Node *expression; OperatorNode *initial_assignment; MultiplayerAPI::RPCMode rpc_mode; + int usages; }; struct Constant { Node *expression; @@ -169,6 +173,8 @@ public: struct Signal { StringName name; Vector<StringName> arguments; + int emissions; + int line; }; Vector<ClassNode *> subclasses; @@ -197,12 +203,16 @@ public: bool _static; MultiplayerAPI::RPCMode rpc_mode; bool has_yield; + bool has_unreachable_code; StringName name; DataType return_type; Vector<StringName> arguments; Vector<DataType> argument_types; Vector<Node *> default_values; BlockNode *body; +#ifdef DEBUG_ENABLED + Vector<int> arguments_usage; +#endif // DEBUG_ENABLED virtual DataType get_datatype() const { return return_type; } virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; } @@ -212,6 +222,7 @@ public: _static = false; rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; has_yield = false; + has_unreachable_code = false; } }; @@ -267,6 +278,7 @@ public: Node *assign; OperatorNode *assign_op; int assignments; + int usages; DataType datatype; virtual DataType get_datatype() const { return datatype; } virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; } @@ -275,6 +287,7 @@ public: assign = NULL; assign_op = NULL; assignments = 0; + usages = 0; } }; @@ -518,6 +531,10 @@ private: Set<int> *safe_lines; #endif // DEBUG_ENABLED +#ifdef DEBUG_ENABLED + List<GDScriptWarning> warnings; +#endif // DEBUG_ENABLED + int pending_newline; List<int> tab_level; @@ -550,6 +567,10 @@ private: MultiplayerAPI::RPCMode rpc_mode; void _set_error(const String &p_error, int p_line = -1, int p_column = -1); +#ifdef DEBUG_ENABLED + void _add_warning(int p_code, int p_line = -1, const String &p_symbol1 = String(), const String &p_symbol2 = String(), const String &p_symbol3 = String(), const String &p_symbol4 = String()); + void _add_warning(int p_code, int p_line, const Vector<String> &p_symbols); +#endif // DEBUG_ENABLED bool _recover_from_completion(); bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false); @@ -605,6 +626,9 @@ public: String get_error() const; int get_error_line() const; int get_error_column() const; +#ifdef DEBUG_ENABLED + const List<GDScriptWarning> &get_warnings() const { return warnings; } +#endif // DEBUG_ENABLED Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL); Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = ""); diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 940bdcbc8d..537a0c5eaf 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -526,8 +526,13 @@ void GDScriptTokenizerText::_advance() { return; } case '#': { // line comment skip - +#ifdef DEBUG_ENABLED + String comment; +#endif // DEBUG_ENABLED while (GETCHAR(0) != '\n') { +#ifdef DEBUG_ENABLED + comment += GETCHAR(0); +#endif // DEBUG_ENABLED code_pos++; if (GETCHAR(0) == 0) { //end of file //_make_error("Unterminated Comment"); @@ -535,6 +540,17 @@ void GDScriptTokenizerText::_advance() { return; } } +#ifdef DEBUG_ENABLED + if (comment.begins_with("#warning-ignore:")) { + String code = comment.get_slice(":", 1); + warning_skips.push_back(Pair<int, String>(line, code.strip_edges().to_lower())); + } else if (comment.begins_with("#warning-ignore-all:")) { + String code = comment.get_slice(":", 1); + warning_global_skips.insert(code.strip_edges().to_lower()); + } else if (comment.strip_edges() == "#warnings-disable") { + ignore_warnings = true; + } +#endif // DEBUG_ENABLED INCPOS(1); column = 1; line++; @@ -1045,6 +1061,9 @@ void GDScriptTokenizerText::set_code(const String &p_code) { column = 1; //the same holds for columns tk_rb_pos = 0; error_flag = false; +#ifdef DEBUG_ENABLED + ignore_warnings = false; +#endif // DEBUG_ENABLED last_error = ""; for (int i = 0; i < MAX_LOOKAHEAD + 1; i++) _advance(); @@ -1172,15 +1191,15 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) Vector<uint8_t> cs; cs.resize(len); for (int j = 0; j < len; j++) { - cs[j] = b[j] ^ 0xb6; + cs.write[j] = b[j] ^ 0xb6; } - cs[cs.size() - 1] = 0; + cs.write[cs.size() - 1] = 0; String s; s.parse_utf8((const char *)cs.ptr()); b += len; total_len -= len + 4; - identifiers[i] = s; + identifiers.write[i] = s; } constants.resize(constant_count); @@ -1193,7 +1212,7 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) return err; b += len; total_len -= len; - constants[i] = v; + constants.write[i] = v; } ERR_FAIL_COND_V(line_count * 8 > total_len, ERR_INVALID_DATA); @@ -1218,10 +1237,10 @@ Error GDScriptTokenizerBuffer::set_code_buffer(const Vector<uint8_t> &p_buffer) if ((*b) & TOKEN_BYTE_MASK) { //little endian always ERR_FAIL_COND_V(total_len < 4, ERR_INVALID_DATA); - tokens[i] = decode_uint32(b) & ~TOKEN_BYTE_MASK; + tokens.write[i] = decode_uint32(b) & ~TOKEN_BYTE_MASK; b += 4; } else { - tokens[i] = *b; + tokens.write[i] = *b; b += 1; total_len--; } @@ -1320,15 +1339,15 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) //save header buf.resize(24); - buf[0] = 'G'; - buf[1] = 'D'; - buf[2] = 'S'; - buf[3] = 'C'; - encode_uint32(BYTECODE_VERSION, &buf[4]); - encode_uint32(identifier_map.size(), &buf[8]); - encode_uint32(constant_map.size(), &buf[12]); - encode_uint32(line_map.size(), &buf[16]); - encode_uint32(token_array.size(), &buf[20]); + buf.write[0] = 'G'; + buf.write[1] = 'D'; + buf.write[2] = 'S'; + buf.write[3] = 'C'; + encode_uint32(BYTECODE_VERSION, &buf.write[4]); + encode_uint32(identifier_map.size(), &buf.write[8]); + encode_uint32(constant_map.size(), &buf.write[12]); + encode_uint32(line_map.size(), &buf.write[16]); + encode_uint32(token_array.size(), &buf.write[20]); //save identifiers @@ -1360,7 +1379,7 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code) ERR_FAIL_COND_V(err != OK, Vector<uint8_t>()); int pos = buf.size(); buf.resize(pos + len); - encode_variant(E->get(), &buf[pos], len); + encode_variant(E->get(), &buf.write[pos], len); } for (Map<int, uint32_t>::Element *E = rev_line_map.front(); E; E = E->next()) { diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 5bd303224c..28a08bfaf8 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -31,6 +31,7 @@ #ifndef GDSCRIPT_TOKENIZER_H #define GDSCRIPT_TOKENIZER_H +#include "core/pair.h" #include "gdscript_functions.h" #include "string_db.h" #include "ustring.h" @@ -171,6 +172,11 @@ public: virtual int get_token_line_indent(int p_offset = 0) const = 0; virtual String get_token_error(int p_offset = 0) const = 0; virtual void advance(int p_amount = 1) = 0; +#ifdef DEBUG_ENABLED + virtual const Vector<Pair<int, String> > &get_warning_skips() const = 0; + virtual const Set<String> &get_warning_global_skips() const = 0; + virtual const bool is_ignoring_warnings() const = 0; +#endif // DEBUG_ENABLED virtual ~GDScriptTokenizer(){}; }; @@ -190,6 +196,7 @@ class GDScriptTokenizerText : public GDScriptTokenizer { union { Variant::Type vtype; //for type types GDScriptFunctions::Function func; //function for built in functions + int warning_code; //for warning skip }; int line, col; TokenData() { @@ -217,6 +224,11 @@ class GDScriptTokenizerText : public GDScriptTokenizer { int tk_rb_pos; String last_error; bool error_flag; +#ifdef DEBUG_ENABLED + Vector<Pair<int, String> > warning_skips; + Set<String> warning_global_skips; + bool ignore_warnings; +#endif // DEBUG_ENABLED void _advance(); @@ -232,6 +244,11 @@ public: virtual const Variant &get_token_constant(int p_offset = 0) const; virtual String get_token_error(int p_offset = 0) const; virtual void advance(int p_amount = 1); +#ifdef DEBUG_ENABLED + virtual const Vector<Pair<int, String> > &get_warning_skips() const { return warning_skips; } + virtual const Set<String> &get_warning_global_skips() const { return warning_global_skips; } + virtual const bool is_ignoring_warnings() const { return ignore_warnings; } +#endif // DEBUG_ENABLED }; class GDScriptTokenizerBuffer : public GDScriptTokenizer { @@ -265,6 +282,11 @@ public: virtual const Variant &get_token_constant(int p_offset = 0) const; virtual String get_token_error(int p_offset = 0) const; virtual void advance(int p_amount = 1); +#ifdef DEBUG_ENABLED + virtual const Vector<Pair<int, String> > &get_warning_skips() const { return Vector<Pair<int, String> >(); } + virtual const Set<String> &get_warning_global_skips() const { return Set<String>(); } + virtual const bool is_ignoring_warnings() const { return true; } +#endif // DEBUG_ENABLED GDScriptTokenizerBuffer(); }; diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 234a59e516..0c5df57d49 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -508,7 +508,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) { continue; PhysicsServer::get_singleton()->body_add_shape(g.static_body, shapes[i].shape->get_rid(), xform * shapes[i].local_transform); if (g.collision_debug.is_valid()) { - shapes[i].shape->add_vertices_to_array(col_debug, xform * shapes[i].local_transform); + shapes.write[i].shape->add_vertices_to_array(col_debug, xform * shapes[i].local_transform); } //print_line("PHIS x: "+xform); @@ -758,7 +758,7 @@ void GridMap::_update_visibility() { for (Map<OctantKey, Octant *>::Element *e = octant_map.front(); e; e = e->next()) { Octant *octant = e->value(); for (int i = 0; i < octant->multimesh_instances.size(); i++) { - Octant::MultimeshInstance &mi = octant->multimesh_instances[i]; + const Octant::MultimeshInstance &mi = octant->multimesh_instances[i]; VS::get_singleton()->instance_set_visible(mi.instance, is_visible()); } } diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index 40540a857f..d11d7a7ec7 100755 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -20,6 +20,8 @@ if env['builtin_mbedtls']: "camellia.c", "ccm.c", "certs.c", + "chacha20.c", + "chachapoly.c", "cipher.c", "cipher_wrap.c", "cmac.c", @@ -37,6 +39,7 @@ if env['builtin_mbedtls']: "error.c", "gcm.c", "havege.c", + "hkdf.c", "hmac_drbg.c", "md2.c", "md4.c", @@ -45,6 +48,7 @@ if env['builtin_mbedtls']: "md_wrap.c", "memory_buffer_alloc.c", "net_sockets.c", + "nist_kw.c", "oid.c", "padlock.c", "pem.c", @@ -57,6 +61,7 @@ if env['builtin_mbedtls']: "pkwrite.c", "platform.c", "platform_util.c", + "poly1305.c", "ripemd160.c", "rsa.c", "rsa_internal.c", diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py index 4912457e2b..e85fa631dd 100644 --- a/modules/mobile_vr/config.py +++ b/modules/mobile_vr/config.py @@ -1,6 +1,5 @@ def can_build(env, platform): - # should probably change this to only be true on iOS and Android - return False + return True def configure(env): pass diff --git a/modules/mobile_vr/mobile_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 6b1c7eb279..2ec00aa72d 100644 --- a/modules/mobile_vr/mobile_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "mobile_interface.h" +#include "mobile_vr_interface.h" #include "core/os/input.h" #include "core/os/os.h" #include "servers/visual/visual_server_global.h" @@ -279,7 +279,7 @@ bool MobileVRInterface::is_stereo() { return true; }; -bool MobileVRInterface::is_initialized() { +bool MobileVRInterface::is_initialized() const { return (initialized); }; diff --git a/modules/mobile_vr/mobile_interface.h b/modules/mobile_vr/mobile_vr_interface.h index bb84281b46..7b2344f1fe 100644 --- a/modules/mobile_vr/mobile_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -134,7 +134,7 @@ public: virtual StringName get_name() const; virtual int get_capabilities() const; - virtual bool is_initialized(); + virtual bool is_initialized() const; virtual bool initialize(); virtual void uninitialize(); @@ -150,4 +150,4 @@ public: ~MobileVRInterface(); }; -#endif // MOBILE_VR_INTERFACE_H +#endif // !MOBILE_VR_INTERFACE_H diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index 0655727a4a..edddaa87e1 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -30,7 +30,7 @@ #include "register_types.h" -#include "mobile_interface.h" +#include "mobile_vr_interface.h" void register_mobile_vr_types() { ClassDB::register_class<MobileVRInterface>(); diff --git a/modules/mono/SCsub b/modules/mono/SCsub index c69a3c9ba6..a2df83925c 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -61,6 +61,7 @@ env_mono.add_source_files(env.modules_sources, 'utils/*.cpp') if env['tools']: env_mono.add_source_files(env.modules_sources, 'editor/*.cpp') + # NOTE: It is safe to generate this file here, since this is still execute serially make_cs_files_header('glue/cs_files', 'glue/cs_compressed.gen.h') vars = Variables() diff --git a/modules/mono/config.py b/modules/mono/config.py index 9a000a2a72..7f226443a1 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -83,6 +83,7 @@ def configure(env): mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] if env['platform'] == 'windows': + mono_root = None if bits == '32': if os.getenv('MONO32_PREFIX'): mono_root = os.getenv('MONO32_PREFIX') diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 62a6b96bb5..7d7028a7a6 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -121,7 +121,7 @@ void CSharpLanguage::init() { #ifdef TOOLS_ENABLED EditorNode::add_init_callback(&gdsharp_editor_init_callback); - GLOBAL_DEF("mono/export/include_scripts_content", true); + GLOBAL_DEF("mono/export/include_scripts_content", false); #endif } @@ -523,7 +523,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec si.resize(frame_count); for (int i = 0; i < frame_count; i++) { - StackInfo &sif = si[i]; + StackInfo &sif = si.write[i]; MonoObject *frame = mono_array_get(frames, MonoObject *, i); MonoString *file_name; @@ -638,33 +638,41 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft #ifdef TOOLS_ENABLED void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) { - if (gdmono->is_runtime_initialized()) { + if (!gdmono->is_runtime_initialized()) + return; - GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); + GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; - } + String name = ProjectSettings::get_singleton()->get("application/config/name"); + if (name.empty()) { + name = "UnnamedProject"; + } - if (proj_assembly) { - String proj_asm_path = proj_assembly->get_path(); + name += ".dll"; - if (!FileAccess::exists(proj_assembly->get_path())) { - // Maybe it wasn't loaded from the default path, so check this as well - proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); - if (!FileAccess::exists(proj_asm_path)) - return; // No assembly to load - } + if (proj_assembly) { + String proj_asm_path = proj_assembly->get_path(); - if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) - return; // Already up to date - } else { - if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + if (!FileAccess::exists(proj_assembly->get_path())) { + // Maybe it wasn't loaded from the default path, so check this as well + proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); + if (!FileAccess::exists(proj_asm_path)) return; // No assembly to load } + + if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) + return; // Already up to date + } else { + if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + return; // No assembly to load } + if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + return; // The core API assembly to load is invalidated + + if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + return; // The editor API assembly to load is invalidated + #ifndef NO_THREADS lock->lock(); #endif @@ -1609,7 +1617,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> ¶ms) { if (p_delegate->has_attribute(CACHED_CLASS(SignalAttribute))) { - MonoType *raw_type = GDMonoClass::get_raw_type(p_delegate); + MonoType *raw_type = p_delegate->get_mono_type(); if (mono_type_get_type(raw_type) == MONO_TYPE_CLASS) { // Arguments are accessibles as arguments of .Invoke method @@ -1963,8 +1971,6 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call ScriptInstance *CSharpScript::instance_create(Object *p_this) { - ERR_FAIL_COND_V(!valid, NULL); - if (!tool && !ScriptServer::is_scripting_enabled()) { #ifdef TOOLS_ENABLED PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this)); @@ -1981,13 +1987,15 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) { // The project assembly is not loaded ERR_EXPLAIN("Cannot instance script because the project assembly is not loaded. Script: " + get_path()); ERR_FAIL_V(NULL); + } else { + // The project assembly is loaded, but the class could not found + ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); + ERR_FAIL_V(NULL); } - - // The project assembly is loaded, but the class could not found - ERR_EXPLAIN("Cannot instance script because the class '" + name + "' could not be found. Script: " + get_path()); - ERR_FAIL_V(NULL); } + ERR_FAIL_COND_V(!valid, NULL); + if (native) { String native_name = native->get_name(); if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) { @@ -2040,6 +2048,9 @@ void CSharpScript::set_source_code(const String &p_code) { bool CSharpScript::has_method(const StringName &p_method) const { + if (!script_class) + return false; + return script_class->has_fetched_method_unknown_params(p_method); } diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 7f9732c297..363ae59d22 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -292,7 +292,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines = NULL) const { return true; } + /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const { return true; } virtual String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 6fa317ee70..76907451e7 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -100,8 +100,6 @@ #define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot" #define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type #define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array" -#define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" -#define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" #define BINDINGS_GENERATOR_VERSION UINT32_C(2) @@ -731,7 +729,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(INDENT1 "public "); bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' - output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class ")); + output.push_back(itype.is_singleton ? "static partial class " : (is_abstract ? "abstract partial class " : "partial class ")); output.push_back(itype.proxy_name); if (itype.is_singleton) { @@ -1338,7 +1336,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } else if (return_type->cs_out.empty()) { p_output.push_back("return " + im_call + ";\n"); } else { - p_output.push_back(INDENT3); p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); p_output.push_back("\n"); } @@ -1653,7 +1650,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n" "\t\t" C_LOCAL_PTRCALL_ARGS ".set("); p_output.push_back(real_argc_str); - p_output.push_back(" + i, &varargs[i]);\n\t" CLOSE_BLOCK); + p_output.push_back(" + i, &varargs.write[i]);\n\t" CLOSE_BLOCK); } else { p_output.push_back(c_in_statements); p_output.push_back("\tconst void* " C_LOCAL_PTRCALL_ARGS "["); @@ -2344,7 +2341,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t) - INSERT_ARRAY(Array, object); INSERT_ARRAY(PoolIntArray, int); INSERT_ARRAY_FULL(PoolByteArray, PoolByteArray, byte); @@ -2362,20 +2358,36 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { #undef INSERT_ARRAY + // Array + itype = TypeInterface(); + itype.name = "Array"; + itype.cname = itype.name; + itype.proxy_name = "Array"; + itype.c_out = "\treturn memnew(Array(%1));\n"; + itype.c_type = itype.name; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; + itype.cs_type = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Array(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; + builtin_types.insert(itype.cname, itype); + // Dictionary itype = TypeInterface(); itype.name = "Dictionary"; itype.cname = itype.name; - itype.proxy_name = "Dictionary<object, object>"; - itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_DICT "(%1);\n"; - itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_DICT "(%1);\n"; - itype.c_arg_in = "&%s_in"; + itype.proxy_name = "Dictionary"; + itype.c_out = "\treturn memnew(Dictionary(%1));\n"; itype.c_type = itype.name; - itype.c_type_in = "MonoObject*"; - itype.c_type_out = "MonoObject*"; + itype.c_type_in = itype.c_type + "*"; + itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; - itype.im_type_in = itype.proxy_name; - itype.im_type_out = itype.proxy_name; + itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()"; + itype.cs_out = "return new Dictionary(%0);"; + itype.im_type_in = "IntPtr"; + itype.im_type_out = "IntPtr"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp new file mode 100644 index 0000000000..0551c1991a --- /dev/null +++ b/modules/mono/glue/collections_glue.cpp @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* collections_glue.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "collections_glue.h" + +#include <mono/metadata/exception.h> + +#include "../mono_gd/gd_mono_class.h" + +Array *godot_icall_Array_Ctor() { + return memnew(Array); +} + +void godot_icall_Array_Dtor(Array *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Array_At(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); +} + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value); +} + +int godot_icall_Array_Count(Array *ptr) { + return ptr->size(); +} + +void godot_icall_Array_Add(Array *ptr, MonoObject *item) { + ptr->append(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Clear(Array *ptr) { + ptr->clear(); +} + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1; +} + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) { + int count = ptr->size(); + + if (mono_array_length(array) < (array_index + count)) { + MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + GDMonoUtils::set_pending_exception(exc); + return; + } + + for (int i = 0; i < count; i++) { + MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i)); + mono_array_setref(array, array_index, boxed); + array_index++; + } +} + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) { + return ptr->find(GDMonoMarshal::mono_object_to_variant(item)); +} + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item)); +} + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item) { + int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item)); + if (idx >= 0) { + ptr->remove(idx); + return true; + } + return false; +} + +void godot_icall_Array_RemoveAt(Array *ptr, int index) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return; + } + ptr->remove(index); +} + +Dictionary *godot_icall_Dictionary_Ctor() { + return memnew(Dictionary); +} + +void godot_icall_Dictionary_Dtor(Dictionary *ptr) { + memdelete(ptr); +} + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); +#ifdef DEBUG_ENABLED + CRASH_COND(!exc); +#endif + GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::set_pending_exception((MonoException *)exc); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ret); +} + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { + ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); +} + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr) { + return memnew(Array(ptr->keys())); +} + +Array *godot_icall_Dictionary_Values(Dictionary *ptr) { + return memnew(Array(ptr->values())); +} + +int godot_icall_Dictionary_Count(Dictionary *ptr) { + return ptr->size(); +} + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + Variant *ret = ptr->getptr(varKey); + if (ret != NULL) { + GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists")); + return; + } + ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value); +} + +void godot_icall_Dictionary_Clear(Dictionary *ptr) { + ptr->clear(); +} + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) { + // no dupes + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + return ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value); +} + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) { + return ptr->has(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) { + return ptr->erase_checked(GDMonoMarshal::mono_object_to_variant(key)); +} + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) { + Variant varKey = GDMonoMarshal::mono_object_to_variant(key); + + // no dupes + Variant *ret = ptr->getptr(varKey); + if (ret != NULL && *ret == GDMonoMarshal::mono_object_to_variant(value)) { + ptr->erase_checked(varKey); + return true; + } + + return false; +} + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + *value = NULL; + return false; + } + *value = GDMonoMarshal::variant_to_mono_object(ret); + return true; +} + +void godot_register_collections_icalls() { + mono_add_internal_call("Godot.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); + mono_add_internal_call("Godot.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); + mono_add_internal_call("Godot.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); + mono_add_internal_call("Godot.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); + mono_add_internal_call("Godot.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); + mono_add_internal_call("Godot.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); + mono_add_internal_call("Godot.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear); + mono_add_internal_call("Godot.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains); + mono_add_internal_call("Godot.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo); + mono_add_internal_call("Godot.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf); + mono_add_internal_call("Godot.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); + mono_add_internal_call("Godot.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); + mono_add_internal_call("Godot.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); + mono_add_internal_call("Godot.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); +} diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h new file mode 100644 index 0000000000..eb5ecfb725 --- /dev/null +++ b/modules/mono/glue/collections_glue.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/* collections_glue.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COLLECTIONS_GLUE_H +#define COLLECTIONS_GLUE_H + +#include "core/array.h" + +#include "../mono_gd/gd_mono_marshal.h" + +// Array + +Array *godot_icall_Array_Ctor(); + +void godot_icall_Array_Dtor(Array *ptr); + +MonoObject *godot_icall_Array_At(Array *ptr, int index); + +void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); + +int godot_icall_Array_Count(Array *ptr); + +void godot_icall_Array_Add(Array *ptr, MonoObject *item); + +void godot_icall_Array_Clear(Array *ptr); + +bool godot_icall_Array_Contains(Array *ptr, MonoObject *item); + +void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index); + +int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item); + +void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item); + +bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); + +void godot_icall_Array_RemoveAt(Array *ptr, int index); + +// Dictionary + +Dictionary *godot_icall_Dictionary_Ctor(); + +void godot_icall_Dictionary_Dtor(Dictionary *ptr); + +MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key); + +void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value); + +Array *godot_icall_Dictionary_Keys(Dictionary *ptr); + +Array *godot_icall_Dictionary_Values(Dictionary *ptr); + +int godot_icall_Dictionary_Count(Dictionary *ptr); + +void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value); + +void godot_icall_Dictionary_Clear(Dictionary *ptr); + +bool godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key); + +bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value); + +bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); + +// Register internal calls + +void godot_register_collections_icalls(); + +#endif // COLLECTIONS_GLUE_H diff --git a/modules/mono/glue/cs_files/Array.cs b/modules/mono/glue/cs_files/Array.cs new file mode 100644 index 0000000000..51f57daef4 --- /dev/null +++ b/modules/mono/glue/cs_files/Array.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class ArraySafeHandle : SafeHandle + { + public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Array.godot_icall_Array_Dtor(handle); + return true; + } + } + + public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + + ArraySafeHandle safeHandle; + bool disposed = false; + + public Array() + { + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + } + + internal Array(ArraySafeHandle handle) + { + safeHandle = handle; + } + + internal Array(IntPtr handle) + { + safeHandle = new ArraySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[int index] + { + get + { + return godot_icall_Array_At(GetPtr(), index); + } + set + { + godot_icall_Array_SetAt(GetPtr(), index, value); + } + } + + public int Count + { + get + { + return godot_icall_Array_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object item) + { + godot_icall_Array_Add(GetPtr(), item); + } + + public void Clear() + { + godot_icall_Array_Clear(GetPtr()); + } + + public bool Contains(object item) + { + return godot_icall_Array_Contains(GetPtr(), item); + } + + public void CopyTo(object[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // Internal call may throw ArgumentException + godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + } + + public IEnumerator<object> GetEnumerator() + { + int count = Count; + + for (int i = 0; i < count; i++) + { + yield return godot_icall_Array_At(GetPtr(), i); + } + } + + public int IndexOf(object item) + { + return godot_icall_Array_IndexOf(GetPtr(), item); + } + + public void Insert(int index, object item) + { + godot_icall_Array_Insert(GetPtr(), index, item); + } + + public bool Remove(object item) + { + return godot_icall_Array_Remove(GetPtr(), item); + } + + public void RemoveAt(int index) + { + godot_icall_Array_RemoveAt(GetPtr(), index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + { + Array objectArray; + + public Array() + { + objectArray = new Array(); + } + + public Array(Array array) + { + objectArray = array; + } + + internal Array(IntPtr handle) + { + objectArray = new Array(handle); + } + + internal Array(ArraySafeHandle handle) + { + objectArray = new Array(handle); + } + + public static explicit operator Array(Array<T> from) + { + return from.objectArray; + } + + public T this[int index] + { + get + { + return (T)objectArray[index]; + } + set + { + objectArray[index] = value; + } + } + + public int Count + { + get + { + return objectArray.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectArray.IsReadOnly; + } + } + + public void Add(T item) + { + objectArray.Add(item); + } + + public void Clear() + { + objectArray.Clear(); + } + + public bool Contains(T item) + { + return objectArray.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // TODO This may be quite slow because every element access is an internal call. + // It could be moved entirely to an internal call if we find out how to do the cast there. + + int count = objectArray.Count; + + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = (T)objectArray[i]; + arrayIndex++; + } + } + + public IEnumerator<T> GetEnumerator() + { + int count = objectArray.Count; + + for (int i = 0; i < count; i++) + { + yield return (T)objectArray[i]; + } + } + + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public bool Remove(T item) + { + return objectArray.Remove(item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs index e0d6d27840..1195071bd3 100644 --- a/modules/mono/glue/cs_files/Color.cs +++ b/modules/mono/glue/cs_files/Color.cs @@ -293,28 +293,80 @@ namespace Godot return res; } - public int ToRgba32() + public int ToAbgr32() { - int c = (byte)(r * 255); + int c = (byte)Math.Round(a * 255); c <<= 8; - c |= (byte)(g * 255); + c |= (byte)Math.Round(b * 255); c <<= 8; - c |= (byte)(b * 255); + c |= (byte)Math.Round(g * 255); c <<= 8; - c |= (byte)(a * 255); + c |= (byte)Math.Round(r * 255); + + return c; + } + + public long ToAbgr64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); return c; } public int ToArgb32() { - int c = (byte)(a * 255); + int c = (byte)Math.Round(a * 255); + c <<= 8; + c |= (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + + return c; + } + + public long ToArgb64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + + return c; + } + + public int ToRgba32() + { + int c = (byte)Math.Round(r * 255); c <<= 8; - c |= (byte)(r * 255); + c |= (byte)Math.Round(g * 255); c <<= 8; - c |= (byte)(g * 255); + c |= (byte)Math.Round(b * 255); c <<= 8; - c |= (byte)(b * 255); + c |= (byte)Math.Round(a * 255); + + return c; + } + + public long ToRgba64() + { + long c = (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(a * 65535); return c; } @@ -353,6 +405,17 @@ namespace Godot r = (rgba & 0xFF) / 255.0f; } + public Color(long rgba) + { + a = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + b = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + g = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + r = (rgba & 0xFFFF) / 65535.0f; + } + private static int _parse_col(string str, int ofs) { int ig = 0; @@ -392,7 +455,7 @@ namespace Godot private String _to_hex(float val) { - var v = (int) Mathf.Clamp(val * 255.0f, 0, 255); + int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); var ret = string.Empty; diff --git a/modules/mono/glue/cs_files/Dictionary.cs b/modules/mono/glue/cs_files/Dictionary.cs new file mode 100644 index 0000000000..57a1960ad9 --- /dev/null +++ b/modules/mono/glue/cs_files/Dictionary.cs @@ -0,0 +1,401 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot +{ + class DictionarySafeHandle : SafeHandle + { + public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Dictionary.godot_icall_Dictionary_Dtor(handle); + return true; + } + } + + public class Dictionary : + IDictionary<object, object>, + ICollection<KeyValuePair<object, object>>, + IEnumerable<KeyValuePair<object, object>>, + IDisposable + { + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + + DictionarySafeHandle safeHandle; + bool disposed = false; + + public Dictionary() + { + safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + } + + internal Dictionary(DictionarySafeHandle handle) + { + safeHandle = handle; + } + + internal Dictionary(IntPtr handle) + { + safeHandle = new DictionarySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[object key] + { + get + { + return godot_icall_Dictionary_GetValue(GetPtr(), key); + } + set + { + godot_icall_Dictionary_SetValue(GetPtr(), key, value); + } + } + + public ICollection<object> Keys + { + get + { + IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public ICollection<object> Values + { + get + { + IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return godot_icall_Dictionary_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object key, object value) + { + godot_icall_Dictionary_Add(GetPtr(), key, value); + } + + public void Add(KeyValuePair<object, object> item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + godot_icall_Dictionary_Clear(GetPtr()); + } + + public bool Contains(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); + } + + public bool ContainsKey(object key) + { + return godot_icall_Dictionary_ContainsKey(GetPtr(), key); + } + + public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<object, object>(keys[i], values[i]); + } + } + + public bool Remove(object key) + { + return godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool Remove(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + } + + public bool TryGetValue(object key, out object value) + { + object retValue; + bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); + value = found ? retValue : default(object); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + + public class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue>, + ICollection<KeyValuePair<TKey, TValue>>, + IEnumerable<KeyValuePair<TKey, TValue>> + { + Dictionary objectDict; + + public Dictionary() + { + objectDict = new Dictionary(); + } + + public Dictionary(Dictionary dictionary) + { + objectDict = dictionary; + } + + internal Dictionary(IntPtr handle) + { + objectDict = new Dictionary(handle); + } + + internal Dictionary(DictionarySafeHandle handle) + { + objectDict = new Dictionary(handle); + } + + public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + { + return from.objectDict; + } + + public TValue this[TKey key] + { + get + { + return (TValue)objectDict[key]; + } + set + { + objectDict[key] = value; + } + } + + public ICollection<TKey> Keys + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + return new Array<TKey>(new ArraySafeHandle(handle)); + } + } + + public ICollection<TValue> Values + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + return new Array<TValue>(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return objectDict.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectDict.IsReadOnly; + } + } + + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public void Add(KeyValuePair<TKey, TValue> item) + { + objectDict.Add(item.Key, item.Value); + } + + public void Clear() + { + objectDict.Clear(); + } + + public bool Contains(KeyValuePair<TKey, TValue> item) + { + return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool ContainsKey(TKey key) + { + return objectDict.ContainsKey(key); + } + + public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]); + } + } + + public bool Remove(TKey key) + { + return objectDict.Remove(key); + } + + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = objectDict.TryGetValue(key, out retValue); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index e2457ff98b..43de9156f2 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -64,6 +64,11 @@ namespace Godot return ResourceLoader.Load(path); } + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) ResourceLoader.Load(path); + } + public static void Print(params object[] what) { NativeCalls.godot_icall_Godot_print(what); diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs index ff4477cc6c..6ad4b3dcb2 100644 --- a/modules/mono/glue/cs_files/MarshalUtils.cs +++ b/modules/mono/glue/cs_files/MarshalUtils.cs @@ -1,36 +1,17 @@ using System; -using System.Collections.Generic; namespace Godot { - internal static class MarshalUtils + static class MarshalUtils { - private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values) + static bool IsArrayGenericType(Type type) { - var ret = new Dictionary<object, object>(); - - for (int i = 0; i < keys.Length; i++) - { - ret.Add(keys[i], values[i]); - } - - return ret; - } - - private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo) - { - var keys = from.Keys; - keysTo = new object[keys.Count]; - keys.CopyTo(keysTo, 0); - - var values = from.Values; - valuesTo = new object[values.Count]; - values.CopyTo(valuesTo, 0); + return type.GetGenericTypeDefinition() == typeof(Array<>); } - private static Type GetDictionaryType() + static bool IsDictionaryGenericType(Type type) { - return typeof(Dictionary<object, object>); + return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); } } } diff --git a/modules/mono/glue/cs_files/NodeExtensions.cs b/modules/mono/glue/cs_files/NodeExtensions.cs new file mode 100644 index 0000000000..71534d7782 --- /dev/null +++ b/modules/mono/glue/cs_files/NodeExtensions.cs @@ -0,0 +1,45 @@ +namespace Godot +{ + public partial class Node + { + public T GetNode<T>(NodePath path) where T : Godot.Node + { + return (T)GetNode(path); + } + + public T GetNodeOrNull<T>(NodePath path) where T : Godot.Node + { + return GetNode(path) as T; + } + + public T GetChild<T>(int idx) where T : Godot.Node + { + return (T)GetChild(idx); + } + + public T GetChildOrNull<T>(int idx) where T : Godot.Node + { + return GetChild(idx) as T; + } + + public T GetOwner<T>() where T : Godot.Node + { + return (T)GetOwner(); + } + + public T GetOwnerOrNull<T>() where T : Godot.Node + { + return GetOwner() as T; + } + + public T GetParent<T>() where T : Godot.Node + { + return (T)GetParent(); + } + + public T GetParentOrNull<T>() where T : Godot.Node + { + return GetParent() as T; + } + } +} diff --git a/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..ceecc589e6 --- /dev/null +++ b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) Load(path); + } + } +} diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt index 7ed6ff82de..7f8f011eb7 100755 --- a/modules/mono/glue/cs_files/VERSION.txt +++ b/modules/mono/glue/cs_files/VERSION.txt @@ -1 +1 @@ -5 +7 diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h index cedc8e9992..6a6f3062b4 100644 --- a/modules/mono/glue/glue_header.h +++ b/modules/mono/glue/glue_header.h @@ -29,6 +29,7 @@ /*************************************************************************/ #include "builtin_types_glue.h" +#include "collections_glue.h" #include "../csharp_script.h" #include "../mono_gd/gd_mono_class.h" @@ -308,4 +309,5 @@ MonoObject *godot_icall_Godot_weakref(Object *p_obj) { void godot_register_header_icalls() { godot_register_builtin_type_icalls(); + godot_register_collections_icalls(); } diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 0f4e211be5..f564b93f8f 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -77,12 +77,12 @@ void setup_runtime_main_args() { Vector<char *> main_args; main_args.resize(cmdline_args.size() + 1); - main_args[0] = execpath.ptrw(); + main_args.write[0] = execpath.ptrw(); int i = 1; for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get(); - main_args[i] = stored.ptrw(); + main_args.write[i] = stored.ptrw(); i++; } diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 66339d7ae6..4e515cde28 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -33,23 +33,35 @@ #include <mono/metadata/attrdefs.h> #include "gd_mono_assembly.h" +#include "gd_mono_marshal.h" -MonoType *GDMonoClass::get_raw_type(GDMonoClass *p_class) { +String GDMonoClass::get_full_name(MonoClass *p_mono_class) { + // mono_type_get_full_name is not exposed to embedders, but this seems to do the job + MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); - return mono_class_get_type(p_class->get_mono_ptr()); -} + MonoException *exc = NULL; + MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); -bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + return GDMonoMarshal::mono_string_to_godot(str); +} - return mono_class_is_assignable_from(mono_class, p_from->mono_class); +MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) { + return mono_class_get_type(p_mono_class); } String GDMonoClass::get_full_name() const { + return get_full_name(mono_class); +} - String res = namespace_name; - if (res.length()) - res += "."; - return res + class_name; +MonoType *GDMonoClass::get_mono_type() { + // Care, you cannot compare MonoType pointers + return get_mono_type(mono_class); +} + +bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { + + return mono_class_is_assignable_from(mono_class, p_from->mono_class); } GDMonoClass *GDMonoClass::get_parent_class() { @@ -308,6 +320,12 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo return get_method(method); } +void *GDMonoClass::get_method_thunk(const StringName &p_name, int p_params_count) { + + GDMonoMethod *method = get_method(p_name, p_params_count); + return method ? method->get_thunk() : NULL; +} + GDMonoField *GDMonoClass::get_field(const StringName &p_name) { Map<StringName, GDMonoField *>::Element *result = fields.find(p_name); @@ -483,7 +501,7 @@ GDMonoClass::~GDMonoClass() { } } - deleted_methods[offset] = method; + deleted_methods.write[offset] = method; ++offset; memdelete(method); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 417c138594..f4e386549a 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -98,7 +98,11 @@ class GDMonoClass { GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly); public: - static MonoType *get_raw_type(GDMonoClass *p_class); + static String get_full_name(MonoClass *p_mono_class); + static MonoType *get_mono_type(MonoClass *p_mono_class); + + String get_full_name() const; + MonoType *get_mono_type(); bool is_assignable_from(GDMonoClass *p_from) const; @@ -108,8 +112,6 @@ public: _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; } _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } - String get_full_name() const; - GDMonoClass *get_parent_class(); #ifdef TOOLS_ENABLED @@ -131,6 +133,8 @@ public: GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count); GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace); + void *get_method_thunk(const StringName &p_name, int p_params_count = 0); + GDMonoField *get_field(const StringName &p_name); const Vector<GDMonoField *> &get_all_fields(); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 3b91777ed4..d3a673dc1b 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -148,7 +148,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) SET_FROM_ARRAY_AND_BREAK(Array); @@ -200,6 +200,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + if (CACHED_CLASS(Dictionary) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + if (CACHED_CLASS(Array) == type_class) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); ERR_FAIL(); } break; @@ -248,10 +260,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } case Variant::DICTIONARY: { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + mono_field_set_value(p_object, mono_field, managed); + } break; + case Variant::ARRAY: { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); } break; - case Variant::ARRAY: SET_FROM_ARRAY_AND_BREAK(Array); case Variant::POOL_BYTE_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolByteArray); case Variant::POOL_INT_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolIntArray); case Variant::POOL_REAL_ARRAY: SET_FROM_ARRAY_AND_BREAK(PoolRealArray); @@ -265,8 +280,28 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - MonoObject *managed = GDMonoMarshal::Dictionary_to_mono_object(p_value.operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 2b5110f0b9..72a5439044 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -45,7 +45,8 @@ struct ManagedType { GDMonoClass *type_class; ManagedType() { - type_class = 0; + type_encoding = 0; + type_class = NULL; } }; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 5cd77d63e2..de91e71bab 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -120,7 +120,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return Variant::ARRAY; @@ -162,12 +162,36 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { if (CACHED_CLASS(RID) == type_class) { return Variant::_RID; } + + if (CACHED_CLASS(Dictionary) == type_class) { + return Variant::DICTIONARY; + } + + if (CACHED_CLASS(Array) == type_class) { + return Variant::ARRAY; + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { return Variant::DICTIONARY; } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return Variant::ARRAY; + } } break; default: { @@ -216,6 +240,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var) { ManagedType type; type.type_encoding = MONO_TYPE_OBJECT; + // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding return variant_to_mono_object(p_var, type); } @@ -315,7 +340,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return (MonoObject *)Array_to_mono_array(p_var->operator Array()); @@ -360,6 +385,14 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (CACHED_CLASS(RID) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator RID()); } + + if (CACHED_CLASS(Dictionary) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + } + + if (CACHED_CLASS(Array) == type_class) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } } break; case MONO_TYPE_OBJECT: { // Variant @@ -411,9 +444,9 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *()); } case Variant::DICTIONARY: - return Dictionary_to_mono_object(p_var->operator Dictionary()); + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); case Variant::ARRAY: - return (MonoObject *)Array_to_mono_array(p_var->operator Array()); + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); case Variant::POOL_BYTE_ARRAY: return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray()); case Variant::POOL_INT_ARRAY: @@ -433,8 +466,24 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_mono_ptr()) { - return Dictionary_to_mono_object(p_var->operator Dictionary()); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + + MonoException *exc = NULL; + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); + } + + exc = NULL; + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } } break; } break; @@ -452,7 +501,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_object_get_class(p_obj)); ERR_FAIL_COND_V(!tclass, Variant()); - MonoType *raw_type = tclass->get_raw_type(tclass); + MonoType *raw_type = tclass->get_mono_type(); ManagedType type; @@ -531,7 +580,7 @@ Variant mono_object_to_variant(MonoObject *p_obj) { case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: { - MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(type.type_class)); + MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type()); if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) return mono_array_to_Array((MonoArray *)p_obj); @@ -579,11 +628,51 @@ Variant mono_object_to_variant(MonoObject *p_obj) { RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj)); return ptr ? Variant(*ptr) : Variant(); } + + if (CACHED_CLASS(Array) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Array_GetPtr get_ptr = CACHED_METHOD_THUNK(Array, GetPtr); + Array *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } + + if (CACHED_CLASS(Dictionary) == type_class) { + MonoException *exc = NULL; + GDMonoUtils::Dictionary_GetPtr get_ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr); + Dictionary *ptr = get_ptr(p_obj, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return ptr ? Variant(*ptr) : Variant(); + } } break; case MONO_TYPE_GENERICINST: { - if (CACHED_RAW_MONO_CLASS(Dictionary) == type.type_class->get_mono_ptr()) { - return mono_object_to_Dictionary(p_obj); + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + + MonoException *exc = NULL; + + GDMonoUtils::IsDictionaryGenericType type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, IsDictionaryGenericType); + MonoBoolean is_dict = type_is_dict((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_dict) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Dictionary *>(ret); + } + + exc = NULL; + + GDMonoUtils::IsArrayGenericType type_is_array = CACHED_METHOD_THUNK(MarshalUtils, IsArrayGenericType); + MonoBoolean is_array = type_is_array((MonoObject *)reftype, (MonoObject **)&exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + if (is_array) { + MonoException *exc = NULL; + MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return *unbox<Array *>(ret); } } break; } @@ -822,66 +911,4 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { return ret; } - -MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict) { - MonoArray *keys = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); - MonoArray *values = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_dict.size()); - - int i = 0; - const Variant *dkey = NULL; - while ((dkey = p_dict.next(dkey))) { - mono_array_set(keys, MonoObject *, i, variant_to_mono_object(dkey)); - mono_array_set(values, MonoObject *, i, variant_to_mono_object(p_dict[*dkey])); - i++; - } - - GDMonoUtils::MarshalUtils_ArraysToDict arrays_to_dict = CACHED_METHOD_THUNK(MarshalUtils, ArraysToDictionary); - - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = arrays_to_dict(keys, values, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(NULL); - } - - return ret; -} - -Dictionary mono_object_to_Dictionary(MonoObject *p_dict) { - Dictionary ret; - - if (!p_dict) - return ret; - - GDMonoUtils::MarshalUtils_DictToArrays dict_to_arrays = CACHED_METHOD_THUNK(MarshalUtils, DictionaryToArrays); - - MonoArray *keys = NULL; - MonoArray *values = NULL; - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - dict_to_arrays(p_dict, &keys, &values, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - - if (exc) { - GDMonoUtils::set_pending_exception(exc); - ERR_FAIL_V(Dictionary()); - } - - int length = mono_array_length(keys); - - for (int i = 0; i < length; i++) { - MonoObject *key_obj = mono_array_get(keys, MonoObject *, i); - MonoObject *value_obj = mono_array_get(values, MonoObject *, i); - - Variant key = key_obj ? mono_object_to_variant(key_obj) : Variant(); - Variant value = value_obj ? mono_object_to_variant(value_obj) : Variant(); - - ret[key] = value; - } - - return ret; -} } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 6572408ab5..464f584a0a 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -143,11 +143,6 @@ PoolVector2Array mono_array_to_PoolVector2Array(MonoArray *p_array); MonoArray *PoolVector3Array_to_mono_array(const PoolVector3Array &p_array); PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array); -// Dictionary - -MonoObject *Dictionary_to_mono_object(const Dictionary &p_dict); -Dictionary mono_object_to_Dictionary(MonoObject *p_dict); - #ifdef YOLO_COPY #define MARSHALLED_OUT(m_t, m_in, m_out) m_t *m_out = (m_t *)&m_in; #define MARSHALLED_IN(m_t, m_in, m_out) m_t m_out = *reinterpret_cast<m_t *>(m_in); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index c8df1038ce..630bda8b4e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -105,9 +105,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, } MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); if (exc) { ret = NULL; @@ -121,9 +119,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, return ret; } else { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); if (exc) { if (r_exc) { @@ -144,9 +140,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index 1f837a2d78..ce66e0c8db 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -140,15 +140,10 @@ bool GDMonoProperty::has_setter() { void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { MonoMethod *prop_method = mono_property_get_set_method(mono_property); - MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); mono_array_set(params, MonoObject *, 0, p_value); - MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_runtime_invoke_array(prop_method, p_object, params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; - + GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); if (exc) { if (r_exc) { *r_exc = exc; @@ -160,9 +155,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoEx void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc); if (exc) { if (r_exc) { @@ -175,9 +168,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoExcept MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index a229552b76..911d629956 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -87,6 +87,8 @@ void MonoCache::clear_members() { method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL; #endif + class_KeyNotFoundException = NULL; + rawclass_Dictionary = NULL; class_Vector2 = NULL; @@ -107,6 +109,8 @@ void MonoCache::clear_members() { class_Control = NULL; class_Spatial = NULL; class_WeakRef = NULL; + class_Array = NULL; + class_Dictionary = NULL; class_MarshalUtils = NULL; #ifdef DEBUG_ENABLED @@ -134,8 +138,10 @@ void MonoCache::clear_members() { field_Image_ptr = NULL; field_RID_ptr = NULL; - methodthunk_MarshalUtils_DictionaryToArrays = NULL; - methodthunk_MarshalUtils_ArraysToDictionary = NULL; + methodthunk_Array_GetPtr = NULL; + methodthunk_Dictionary_GetPtr = NULL; + methodthunk_MarshalUtils_IsArrayGenericType = NULL; + methodthunk_MarshalUtils_IsDictionaryGenericType = NULL; methodthunk_SignalAwaiter_SignalCallback = NULL; methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; @@ -170,11 +176,13 @@ void update_corlib_cache() { #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace")); - CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_thunk("GetFrames")); CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true)); CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true)); #endif + CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException")); + mono_cache.corlib_cache_updated = true; } @@ -198,6 +206,8 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef)); + CACHE_CLASS_AND_CHECK(Array, GODOT_API_CLASS(Array)); + CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_CLASS(Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); #ifdef DEBUG_ENABLED @@ -224,34 +234,21 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD)); CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryToArrays, (MarshalUtils_DictToArrays)CACHED_CLASS(MarshalUtils)->get_method("DictionaryToArrays", 3)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArraysToDictionary, (MarshalUtils_ArraysToDict)CACHED_CLASS(MarshalUtils)->get_method("ArraysToDictionary", 2)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk()); - CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, (Array_GetPtr)GODOT_API_CLASS(Array)->get_method_thunk("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, (Dictionary_GetPtr)GODOT_API_CLASS(Dictionary)->get_method_thunk("GetPtr", 0)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsArrayGenericType, (IsArrayGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsArrayGenericType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IsDictionaryGenericType, (IsDictionaryGenericType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IsDictionaryGenericType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, (SignalAwaiter_SignalCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("SignalCallback", 1)); + CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); + CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); #ifdef DEBUG_ENABLED - CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk()); + CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); #endif - { - /* - * TODO Right now we only support Dictionary<object, object>. - * It would be great if we could support other key/value types - * without forcing the user to copy the entries. - */ - GDMonoMethod *method_get_dict_type = CACHED_CLASS(MarshalUtils)->get_method("GetDictionaryType", 0); - ERR_FAIL_NULL(method_get_dict_type); - MonoReflectionType *dict_refl_type = (MonoReflectionType *)method_get_dict_type->invoke(NULL); - ERR_FAIL_NULL(dict_refl_type); - MonoType *dict_type = mono_reflection_type_get_type(dict_refl_type); - ERR_FAIL_NULL(dict_type); - - CACHE_RAW_MONO_CLASS_AND_CHECK(Dictionary, mono_class_from_mono_type(dict_type)); - } - + // TODO Move to CSharpLanguage::init() MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); - mono_runtime_object_init(task_scheduler); + GDMonoUtils::runtime_object_init(task_scheduler); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); mono_cache.godot_api_cache_updated = true; @@ -304,6 +301,12 @@ MonoThread *get_current_thread() { return mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_runtime_object_init(p_this_obj); + GD_MONO_END_RUNTIME_INVOKE; +} + GDMonoClass *get_object_class(MonoObject *p_object) { return GDMono::get_singleton()->get_class(mono_object_get_class(p_object)); } @@ -358,7 +361,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); return mono_object; } @@ -368,7 +371,7 @@ MonoObject *create_managed_from(const NodePath &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from))); @@ -380,13 +383,73 @@ MonoObject *create_managed_from(const RID &p_from) { ERR_FAIL_NULL_V(mono_object, NULL); // Construct - mono_runtime_object_init(mono_object); + GDMonoUtils::runtime_object_init(mono_object); CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from))); return mono_object; } +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Array *new_array = memnew(Array(p_from)); + void *args[1] = { &new_array }; + + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { + MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + ERR_FAIL_NULL_V(mono_object, NULL); + + // Search constructor that takes a pointer as parameter + MonoMethod *m; + void *iter = NULL; + while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) { + if (strcmp(mono_method_get_name(m), ".ctor") == 0) { + MonoMethodSignature *sig = mono_method_signature(m); + void *front = NULL; + if (mono_signature_get_param_count(sig) == 1 && + mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) { + break; + } + } + } + + CRASH_COND(m == NULL); + + Dictionary *new_dict = memnew(Dictionary(p_from)); + void *args[1] = { &new_dict }; + + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + MonoDomain *create_domain(const String &p_friendly_name) { MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL); @@ -400,10 +463,10 @@ MonoDomain *create_domain(const String &p_friendly_name) { return domain; } -String get_exception_name_and_message(MonoException *p_ex) { +String get_exception_name_and_message(MonoException *p_exc) { String res; - MonoClass *klass = mono_object_get_class((MonoObject *)p_ex); + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); MonoType *type = mono_class_get_type(klass); char *full_name = mono_type_full_name(type); @@ -413,12 +476,20 @@ String get_exception_name_and_message(MonoException *p_ex) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_ex, NULL, NULL); + MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); res += GDMonoMarshal::mono_string_to_godot(msg); return res; } +void set_exception_message(MonoException *p_exc, String message) { + MonoClass *klass = mono_object_get_class((MonoObject *)p_exc); + MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); + MonoString *msg = GDMonoMarshal::mono_string_from_godot(message); + void *params[1] = { msg }; + property_set_value(prop, (MonoObject *)p_exc, params, NULL); +} + void debug_print_unhandled_exception(MonoException *p_exc) { print_unhandled_exception(p_exc); debug_send_unhandled_exception_error(p_exc); @@ -517,4 +588,38 @@ void set_pending_exception(MonoException *p_exc) { _THREAD_LOCAL_(int) current_invoke_count = 0; +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; +} + +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 4f8e5932cd..bf8860c85a 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -41,14 +41,24 @@ #include "object.h" #include "reference.h" +#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ + if (unlikely(m_exc != NULL)) { \ + GDMonoUtils::debug_unhandled_exception(m_exc); \ + _UNREACHABLE_(); \ + } + namespace GDMonoUtils { -typedef MonoObject *(*MarshalUtils_DictToArrays)(MonoObject *, MonoArray **, MonoArray **, MonoObject **); -typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoObject **); +typedef Array *(*Array_GetPtr)(MonoObject *, MonoObject **); +typedef Dictionary *(*Dictionary_GetPtr)(MonoObject *, MonoObject **); typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray *, MonoObject **); typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **); typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **); typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsArrayGenericType)(MonoObject *, MonoObject **); +typedef MonoBoolean (*IsDictionaryGenericType)(MonoObject *, MonoObject **); typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **); struct MonoCache { @@ -79,6 +89,8 @@ struct MonoCache { GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool; #endif + GDMonoClass *class_KeyNotFoundException; + MonoClass *rawclass_Dictionary; // ----------------------------------------------- @@ -100,6 +112,8 @@ struct MonoCache { GDMonoClass *class_Control; GDMonoClass *class_Spatial; GDMonoClass *class_WeakRef; + GDMonoClass *class_Array; + GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; #ifdef DEBUG_ENABLED @@ -127,8 +141,10 @@ struct MonoCache { GDMonoField *field_Image_ptr; GDMonoField *field_RID_ptr; - MarshalUtils_DictToArrays methodthunk_MarshalUtils_DictionaryToArrays; - MarshalUtils_ArraysToDict methodthunk_MarshalUtils_ArraysToDictionary; + Array_GetPtr methodthunk_Array_GetPtr; + Dictionary_GetPtr methodthunk_Dictionary_GetPtr; + IsArrayGenericType methodthunk_MarshalUtils_IsArrayGenericType; + IsDictionaryGenericType methodthunk_MarshalUtils_IsDictionaryGenericType; SignalAwaiter_SignalCallback methodthunk_SignalAwaiter_SignalCallback; SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; @@ -175,6 +191,8 @@ _FORCE_INLINE_ bool is_main_thread() { return mono_domain_get() != NULL && mono_thread_get_main() == mono_thread_current(); } +void runtime_object_init(MonoObject *p_this_obj); + GDMonoClass *get_object_class(MonoObject *p_object); GDMonoClass *type_get_proxy_class(const StringName &p_type); GDMonoClass *get_class_native_base(GDMonoClass *p_class); @@ -183,10 +201,13 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa MonoObject *create_managed_from(const NodePath &p_from); MonoObject *create_managed_from(const RID &p_from); +MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class); +MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class); MonoDomain *create_domain(const String &p_friendly_name); -String get_exception_name_and_message(MonoException *p_ex); +String get_exception_name_and_message(MonoException *p_exc); +void set_exception_message(MonoException *p_exc, String message); void debug_print_unhandled_exception(MonoException *p_exc); void debug_send_unhandled_exception_error(MonoException *p_exc); @@ -209,6 +230,14 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { return current_invoke_count; } +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc); + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc); + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); + } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp index 6f8b0f90bc..ae9f130518 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/utils/thread_local.cpp @@ -63,7 +63,13 @@ struct ThreadLocalStorage::Impl { #endif } - Impl(void (*p_destr_callback_func)(void *)) { +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + + Impl(void (_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) { #ifdef WINDOWS_ENABLED dwFlsIndex = FlsAlloc(p_destr_callback_func); ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); @@ -89,10 +95,12 @@ void ThreadLocalStorage::set_value(void *p_value) const { pimpl->set_value(p_value); } -void ThreadLocalStorage::alloc(void (*p_destr_callback)(void *)) { +void ThreadLocalStorage::alloc(void (_CALLBACK_FUNC_ *p_destr_callback)(void *)) { pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); } +#undef _CALLBACK_FUNC_ + void ThreadLocalStorage::free() { memdelete(pimpl); pimpl = NULL; diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index 7ff10b4efc..783e40dc01 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -65,12 +65,18 @@ #include "core/typedefs.h" +#ifdef WINDOWS_ENABLED +#define _CALLBACK_FUNC_ __stdcall +#else +#define _CALLBACK_FUNC_ +#endif + struct ThreadLocalStorage { void *get_value() const; void set_value(void *p_value) const; - void alloc(void (*p_dest_callback)(void *)); + void alloc(void (_CALLBACK_FUNC_ *p_dest_callback)(void *)); void free(); private: @@ -85,17 +91,10 @@ class ThreadLocal { T init_val; -#ifdef WINDOWS_ENABLED -#define _CALLBACK_FUNC_ __stdcall -#else -#define _CALLBACK_FUNC_ -#endif - static void _CALLBACK_FUNC_ destr_callback(void *tls_data) { memdelete(static_cast<T *>(tls_data)); } -#undef _CALLBACK_FUNC_ T *_tls_get_value() const { void *tls_data = storage.get_value(); @@ -156,6 +155,8 @@ private: bool &flag; }; +#undef _CALLBACK_FUNC_ + #define _TLS_RECURSION_GUARD_V_(m_ret) \ static _THREAD_LOCAL_(bool) _recursion_flag_ = false; \ if (_recursion_flag_) \ diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index 64c4b85269..c1ce2592a4 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -126,9 +126,9 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con for (unsigned int j = 0; j < ntris; j++) { Vector<int> nav_indices; nav_indices.resize(3); - nav_indices[0] = ((int)(bverts + tris[j * 4 + 0])); - nav_indices[1] = ((int)(bverts + tris[j * 4 + 1])); - nav_indices[2] = ((int)(bverts + tris[j * 4 + 2])); + nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0])); + nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 1])); + nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 2])); p_nav_mesh->add_polygon(nav_indices); } } diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 6f2bb46fc8..733f32277b 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -265,8 +265,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) for (uint32_t i = 0; i < size; i++) { - result->data[i].start = ovector[i * 2]; - result->data[i].end = ovector[i * 2 + 1]; + result->data.write[i].start = ovector[i * 2]; + result->data.write[i].end = ovector[i * 2 + 1]; } pcre2_match_data_free_16(match); @@ -295,8 +295,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) for (uint32_t i = 0; i < size; i++) { - result->data[i].start = ovector[i * 2]; - result->data[i].end = ovector[i * 2 + 1]; + result->data.write[i].start = ovector[i * 2]; + result->data.write[i].end = ovector[i * 2 + 1]; } pcre2_match_data_free_32(match); diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp index 0cf24dd8d8..f6be537413 100644 --- a/modules/squish/image_compress_squish.cpp +++ b/modules/squish/image_compress_squish.cpp @@ -46,7 +46,7 @@ void image_decompress_squish(Image *p_image) { Image::Format target_format = Image::FORMAT_RGBA8; PoolVector<uint8_t> data; - int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps()); int mm_count = p_image->get_mipmap_count(); data.resize(target_size); @@ -96,6 +96,33 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { Image::DetectChannels dc = p_image->get_detected_channels(); + if (p_source == Image::COMPRESS_SOURCE_LAYERED) { + //keep what comes in + switch (p_image->get_format()) { + case Image::FORMAT_L8: { + dc = Image::DETECTED_L; + } break; + case Image::FORMAT_LA8: { + dc = Image::DETECTED_LA; + } break; + case Image::FORMAT_R8: { + dc = Image::DETECTED_R; + } break; + case Image::FORMAT_RG8: { + dc = Image::DETECTED_RG; + } break; + case Image::FORMAT_RGB8: { + dc = Image::DETECTED_RGB; + } break; + case Image::FORMAT_RGBA8: + case Image::FORMAT_RGBA4444: + case Image::FORMAT_RGBA5551: { + dc = Image::DETECTED_RGBA; + } break; + default: {} + } + } + p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert if (p_source == Image::COMPRESS_SOURCE_SRGB && (dc == Image::DETECTED_R || dc == Image::DETECTED_RG)) { @@ -148,7 +175,7 @@ void image_compress_squish(Image *p_image, Image::CompressSource p_source) { } PoolVector<uint8_t> data; - int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0); + int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps()); int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0; data.resize(target_size); int shift = Image::get_format_pixel_rshift(target_format); diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index af9fea6681..bbdec7195f 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -771,7 +771,7 @@ void VisualScript::custom_signal_set_argument_type(const StringName &p_func, int ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); - custom_signals[p_func][p_argidx].type = p_type; + custom_signals[p_func].write[p_argidx].type = p_type; } Variant::Type VisualScript::custom_signal_get_argument_type(const StringName &p_func, int p_argidx) const { @@ -783,7 +783,7 @@ void VisualScript::custom_signal_set_argument_name(const StringName &p_func, int ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(!custom_signals.has(p_func)); ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); - custom_signals[p_func][p_argidx].name = p_name; + custom_signals[p_func].write[p_argidx].name = p_name; } String VisualScript::custom_signal_get_argument_name(const StringName &p_func, int p_argidx) const { @@ -811,7 +811,7 @@ void VisualScript::custom_signal_swap_argument(const StringName &p_func, int p_a ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size()); ERR_FAIL_INDEX(p_with_argidx, custom_signals[p_func].size()); - SWAP(custom_signals[p_func][p_argidx], custom_signals[p_func][p_with_argidx]); + SWAP(custom_signals[p_func].write[p_argidx], custom_signals[p_func].write[p_with_argidx]); } void VisualScript::remove_custom_signal(const StringName &p_name) { @@ -1333,6 +1333,19 @@ VisualScript::VisualScript() { base_type = "Object"; } +Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { + List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); + get_sequence_connection_list(edited_func, sc); + Set<int> connected; + for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) { + if (E->get().from_node == from_node) { + connected.insert(E->get().from_output); + } + } + memdelete(sc); + return connected; +} + VisualScript::~VisualScript() { while (!functions.empty()) { @@ -2402,7 +2415,7 @@ void VisualScriptLanguage::make_template(const String &p_class_name, const Strin script->set_instance_base_type(p_base_class_name); } -bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const { +bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { return false; } diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index b163203a3a..13a8b909b0 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -319,6 +319,7 @@ public: void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx); void remove_custom_signal(const StringName &p_name); void rename_custom_signal(const StringName &p_name, const StringName &p_new_name); + Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node); void get_custom_signal_list(List<StringName> *r_custom_signals) const; @@ -563,7 +564,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<ScriptLanguage::Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 873cc293c9..45a27d1e79 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -33,8 +33,10 @@ #include "core/script_language.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" +#include "object.h" #include "os/input.h" #include "os/keyboard.h" +#include "variant.h" #include "visual_script_expression.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" @@ -1293,7 +1295,7 @@ void VisualScriptEditor::_on_nodes_duplicate() { Ref<VisualScriptNode> node = script->get_node(edited_func, F->get()); - Ref<VisualScriptNode> dupe = node->duplicate(); + Ref<VisualScriptNode> dupe = node->duplicate(true); int new_id = idc++; to_select.insert(new_id); @@ -1327,6 +1329,10 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { } } +void VisualScriptEditor::_generic_search() { + new_connect_node_select->select_from_visual_script(String(""), false); +} + void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> key = p_event; @@ -1780,7 +1786,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da call->set_base_type(node->get_class()); n = call; - method_select->select_method_from_instance(node); + method_select->select_from_instance(node); selecting_method_id = base_id; } @@ -1917,7 +1923,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } -void VisualScriptEditor::_selected_method(const String &p_method) { +void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) { Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); if (!vsfc.is_valid()) @@ -1962,22 +1968,16 @@ void VisualScriptEditor::_button_resource_previewed(const String &p_path, const void VisualScriptEditor::apply_code() { } -Ref<Script> VisualScriptEditor::get_edited_script() const { - +RES VisualScriptEditor::get_edited_resource() const { return script; } -Vector<String> VisualScriptEditor::get_functions() { - - return Vector<String>(); -} +void VisualScriptEditor::set_edited_resource(const RES &p_res) { -void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) { - - script = p_script; - signal_editor->script = p_script; + script = p_res; + signal_editor->script = script; signal_editor->undo_redo = undo_redo; - variable_editor->script = p_script; + variable_editor->script = script; variable_editor->undo_redo = undo_redo; script->connect("node_ports_changed", this, "_node_ports_changed"); @@ -1986,6 +1986,11 @@ void VisualScriptEditor::set_edited_script(const Ref<Script> &p_script) { _update_available_nodes(); } +Vector<String> VisualScriptEditor::get_functions() { + + return Vector<String>(); +} + void VisualScriptEditor::reload_text() { } @@ -2436,33 +2441,19 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro if (!vsn.is_valid()) return; - if (p_from_slot < vsn->get_output_sequence_port_count()) { + port_action_pos = p_release_pos; - port_action_popup->clear(); - port_action_popup->add_item(TTR("Condition"), CREATE_COND); - port_action_popup->add_item(TTR("Sequence"), CREATE_SEQUENCE); - port_action_popup->add_item(TTR("Switch"), CREATE_SWITCH); - port_action_popup->add_item(TTR("Iterator"), CREATE_ITERATOR); - port_action_popup->add_item(TTR("While"), CREATE_WHILE); - port_action_popup->add_item(TTR("Return"), CREATE_RETURN); + if (p_from_slot < vsn->get_output_sequence_port_count()) { port_action_node = p_from.to_int(); port_action_output = p_from_slot; - + _port_action_menu(CREATE_ACTION); } else { - port_action_popup->clear(); - port_action_popup->add_item(TTR("Call"), CREATE_CALL); - port_action_popup->add_item(TTR("Get"), CREATE_GET); - port_action_popup->add_item(TTR("Set"), CREATE_SET); port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); + _port_action_menu(CREATE_CALL_SET_GET); } - - port_action_pos = p_release_pos; - port_action_popup->set_size(Size2(1, 1)); - port_action_popup->set_position(graph->get_global_position() + p_release_pos); - port_action_popup->popup(); } VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) { @@ -2530,168 +2521,205 @@ void VisualScriptEditor::_port_action_menu(int p_option) { bool seq_connect = false; - Ref<VisualScriptNode> vnode; Set<int> vn; switch (p_option) { - case CREATE_CALL: { - + case CREATE_CALL_SET_GET: { Ref<VisualScriptFunctionCall> n; n.instance(); - vnode = n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } + if (tg.gdclass != StringName()) { + n->set_base_type(tg.gdclass); + } else { + n->set_base_type("Object"); + } + String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_method_from_script(tg.script); + new_connect_node_select->select_from_script(tg.script, ""); + } else if (type_string != String()) { + new_connect_node_select->select_from_base_type(type_string); } else { - new_connect_node_select->select_method_from_base_type(n->get_base_type()); + new_connect_node_select->select_from_base_type(n->get_base_type()); } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_base_type(""); } else { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_method_from_basic_type(tg.type); + new_connect_node_select->select_from_basic_type(tg.type); } - } break; - case CREATE_SET: { - - Ref<VisualScriptPropertySet> n; - n.instance(); - vnode = n; - + case CREATE_ACTION: { + seq_connect = true; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - + PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } - - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); + if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { + new_connect_node_select->select_from_action(property_info.hint_string); } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); + new_connect_node_select->select_from_action(""); } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_action(""); } else { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } } break; - case CREATE_GET: { + } +} - Ref<VisualScriptPropertyGet> n; - n.instance(); - vnode = n; +void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) { + Set<int> vn; + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph", new_id); + undo_redo->add_undo_method(this, "_update_graph", new_id); + undo_redo->commit_action(); - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + port_action_new_node = new_id; +} - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); +void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) { + undo_redo->create_action(TTR("Connect Node Data")); + VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr()); + if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) { + vnode_return->set_enable_return_value(true); + } + if (vnode_old->get_output_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + if (vnode->get_input_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + int port = port_action_output; + int value_count = vnode_old->get_output_value_port_count(); + if (port >= value_count) { + port = 0; + } + int count = vnode_old->get_output_value_port_count() + vnode_old->get_output_sequence_port_count(); + undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); + undo_redo->commit_action(); +} - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } +void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) { + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); - } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); - } + Set<int> vn; + + if (p_category == "visualscript") { + Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + + if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type); + } + + if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && script->get_node(edited_func, port_action_node).is_valid()) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (type == Variant::OBJECT) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name); + } else if (type == Variant::NIL) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(""); } else { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type)); } + } + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs); + if (vnode_old.is_valid() && p_connecting == true) { + connect_seq(vnode_old, vnode_new, new_id); + connect_data(vnode_old, vnode_new, new_id); + } - } break; - case CREATE_COND: { + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + return; + } + + Ref<VisualScriptNode> vnode; + + seq_connect = false; + if (p_category == String("method")) { + + Ref<VisualScriptFunctionCall> n; + n.instance(); + vnode = n; + } else if (p_category == String("set")) { + + Ref<VisualScriptPropertySet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } else if (p_category == String("get")) { + + Ref<VisualScriptPropertyGet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } + + if (p_category == String("action")) { + if (p_text == "VisualScriptCondition") { Ref<VisualScriptCondition> n; n.instance(); vnode = n; seq_connect = true; + } + if (p_text == "VisualScriptSwitch") { - } break; - case CREATE_SEQUENCE: { - - Ref<VisualScriptSequence> n; + Ref<VisualScriptSwitch> n; n.instance(); vnode = n; seq_connect = true; + } else if (p_text == "VisualScriptSequence") { - } break; - case CREATE_SWITCH: { - - Ref<VisualScriptSwitch> n; + Ref<VisualScriptSequence> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_ITERATOR: { + } else if (p_text == "VisualScriptIterator") { Ref<VisualScriptIterator> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_WHILE: { + } else if (p_text == "VisualScriptWhile") { Ref<VisualScriptWhile> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_RETURN: { + } else if (p_text == "VisualScriptReturn") { Ref<VisualScriptReturn> n; n.instance(); vnode = n; seq_connect = true; - - } break; - } - - int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - if (seq_connect) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); + } } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph", new_id); - undo_redo->add_undo_method(this, "_update_graph", new_id); - undo_redo->commit_action(); - - port_action_new_node = new_id; -} -void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p_text) { + new_node(vnode, ofs); Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); @@ -2699,28 +2727,152 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsfc->set_base_type(tg.gdclass); + + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsfc->set_base_type(base_type); + } + if (p_text == "call" || p_text == "call_deferred") { + vsfc->set_function(String("")); + } + } + if (tg.script.is_valid()) { + vsfc->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(String("")); + } else { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); + vsfc->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + } else { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { - Ref<VisualScriptPropertyGet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); - } + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else if (script->get_node(edited_func, port_action_node).is_valid()) { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(String("")); + } else { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } + } + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + if (vnode_old.is_valid() && p_connecting == true) { + connect_seq(vnode_old, vnode, port_action_new_node); + connect_data(vnode_old, vnode, port_action_new_node); + } _update_graph(port_action_new_node); _update_graph_connections(); } -void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { +void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { + int seq_count = vnode_old->get_output_sequence_port_count(); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); + if (vnode_operator != NULL && vnode_operator->has_input_sequence_port() == false) { + return; + } + VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr()); + if (vnode_constructor != NULL) { + return; + } + if (vnode_old->get_output_sequence_port_count() <= 0) { + return; + } + if (vnode_new->has_input_sequence_port() == false) { + return; + } + VisualScriptFunction *vnode_function = Object::cast_to<VisualScriptFunction>(vnode_old.ptr()); + undo_redo->create_action(TTR("Connect Node Sequence")); + int pass_port = -vnode_old->get_output_sequence_port_count() + 1; + int return_port = port_action_output - 1; + if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id); + } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id); + } else { + for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { + int count = vnode_old->get_output_sequence_port_count(); + if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id); + break; + } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id); + break; + } + } + } + + undo_redo->commit_action(); +} + +void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) { String name = p_text; if (script->has_function(name)) { @@ -2753,7 +2905,7 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { undo_redo->add_do_method(script.ptr(), "add_function", name); for (int i = 0; i < minfo.arguments.size(); i++) { - func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name); + func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string); } undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node); @@ -2777,12 +2929,29 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { _update_graph(); } -void VisualScriptEditor::_cancel_connect_node_method_or_setget() { - - script->remove_node(edited_func, port_action_new_node); +void VisualScriptEditor::_cancel_connect_node() { + // Causes crashes + //script->remove_node(edited_func, port_action_new_node); _update_graph(); } +void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) { + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualScriptEditor::_default_value_changed() { Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id); @@ -2863,7 +3032,8 @@ void VisualScriptEditor::_node_filter_changed(const String &p_text) { void VisualScriptEditor::_notification(int p_what) { if (p_what == NOTIFICATION_READY) { - node_filter->add_icon_override("right_icon", Control::get_icon("Search", "EditorIcons")); + node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); + node_filter->set_clear_button_enabled(true); variable_editor->connect("changed", this, "_update_members"); signal_editor->connect("changed", this, "_update_members"); @@ -2980,9 +3150,7 @@ void VisualScriptEditor::_menu_option(int p_what) { } break; case EDIT_FIND_NODE_TYPE: { - //popup disappearing grabs focus to owner, so use call deferred - node_filter->call_deferred("grab_focus"); - node_filter->call_deferred("select_all"); + _generic_search(); } break; case EDIT_COPY_NODES: case EDIT_CUT_NODES: { @@ -3006,7 +3174,7 @@ void VisualScriptEditor::_menu_option(int p_what) { return; } if (node.is_valid()) { - clipboard->nodes[id] = node->duplicate(); + clipboard->nodes[id] = node->duplicate(true); clipboard->nodes_positions[id] = script->get_node_position(edited_func, id); } } @@ -3286,10 +3454,11 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized); ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed); ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu); - ClassDB::bind_method("_selected_connect_node_method_or_setget", &VisualScriptEditor::_selected_connect_node_method_or_setget); + ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node); ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); - ClassDB::bind_method("_cancel_connect_node_method_or_setget", &VisualScriptEditor::_cancel_connect_node_method_or_setget); + ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node); + ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw); @@ -3318,6 +3487,8 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_member_option", &VisualScriptEditor::_member_option); ClassDB::bind_method("_update_available_nodes", &VisualScriptEditor::_update_available_nodes); + + ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search); } VisualScriptEditor::VisualScriptEditor() { @@ -3480,25 +3651,21 @@ VisualScriptEditor::VisualScriptEditor() { add_child(default_value_edit); default_value_edit->connect("variant_changed", this, "_default_value_changed"); - method_select = memnew(PropertySelector); + method_select = memnew(VisualScriptPropertySelector); add_child(method_select); method_select->connect("selected", this, "_selected_method"); error_line = -1; - new_connect_node_select = memnew(PropertySelector); + new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); - new_connect_node_select->connect("selected", this, "_selected_connect_node_method_or_setget"); - new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node_method_or_setget"); + new_connect_node_select->connect("selected", this, "_selected_connect_node"); + new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node"); - new_virtual_method_select = memnew(PropertySelector); + new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method"); - port_action_popup = memnew(PopupMenu); - add_child(port_action_popup); - port_action_popup->connect("id_pressed", this, "_port_action_menu"); - member_popup = memnew(PopupMenu); add_child(member_popup); members->connect("item_rmb_selected", this, "_member_rmb_selected"); @@ -3515,9 +3682,9 @@ VisualScriptEditor::~VisualScriptEditor() { memdelete(variable_editor); } -static ScriptEditorBase *create_editor(const Ref<Script> &p_script) { +static ScriptEditorBase *create_editor(const RES &p_resource) { - if (Object::cast_to<VisualScript>(*p_script)) { + if (Object::cast_to<VisualScript>(*p_resource)) { return memnew(VisualScriptEditor); } diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 0bd64d6a1d..8bfd147519 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -34,9 +34,9 @@ #include "editor/create_dialog.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/property_editor.h" -#include "editor/property_selector.h" #include "scene/gui/graph_edit.h" #include "visual_script.h" +#include "visual_script_property_selector.h" class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; @@ -62,15 +62,8 @@ class VisualScriptEditor : public ScriptEditorBase { enum PortAction { - CREATE_CALL, - CREATE_SET, - CREATE_GET, - CREATE_COND, - CREATE_SEQUENCE, - CREATE_SWITCH, - CREATE_ITERATOR, - CREATE_WHILE, - CREATE_RETURN, + CREATE_CALL_SET_GET, + CREATE_ACTION, }; enum MemberAction { @@ -102,9 +95,9 @@ class VisualScriptEditor : public ScriptEditorBase { AcceptDialog *edit_signal_dialog; PropertyEditor *edit_signal_edit; - PropertySelector *method_select; - PropertySelector *new_connect_node_select; - PropertySelector *new_virtual_method_select; + VisualScriptPropertySelector *method_select; + VisualScriptPropertySelector *new_connect_node_select; + VisualScriptPropertySelector *new_virtual_method_select; VisualScriptEditorVariableEdit *variable_editor; @@ -162,21 +155,29 @@ class VisualScriptEditor : public ScriptEditorBase { static Clipboard *clipboard; - PopupMenu *port_action_popup; PopupMenu *member_popup; - MemberType member_type; String member_name; + bool seq_connect = false; + PortAction port_action; int port_action_node; int port_action_output; Vector2 port_action_pos; int port_action_new_node; void _port_action_menu(int p_option); - void _selected_connect_node_method_or_setget(const String &p_text); - void _cancel_connect_node_method_or_setget(); - void _selected_new_virtual_method(const String &p_text); + + void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs); + + void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); + + void _selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting = true); + void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); + + void _cancel_connect_node(); + void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point); + void _selected_new_virtual_method(const String &p_text = String(""), const String &p_category = String(""), const bool p_connecting = true); int error_line; @@ -211,6 +212,9 @@ class VisualScriptEditor : public ScriptEditorBase { String revert_on_drag; void _input(const Ref<InputEvent> &p_event); + + void _generic_search(); + void _members_gui_input(const Ref<InputEvent> &p_event); void _on_nodes_delete(); void _on_nodes_duplicate(); @@ -231,7 +235,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _comment_node_resized(const Vector2 &p_new_size, int p_node); int selecting_method_id; - void _selected_method(const String &p_method); + void _selected_method(const String &p_method, const String &p_type); void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud); @@ -250,9 +254,9 @@ public: virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void apply_code(); - virtual Ref<Script> get_edited_script() const; + virtual RES get_edited_resource() const; + virtual void set_edited_resource(const RES &p_res); virtual Vector<String> get_functions(); - virtual void set_edited_script(const Ref<Script> &p_script); virtual void reload_text(); virtual String get_name(); virtual Ref<Texture> get_icon(); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index d5f9d21348..868d22b541 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -56,11 +56,11 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val int from = inputs.size(); inputs.resize(int(p_value)); for (int i = from; i < inputs.size(); i++) { - inputs[i].name = String::chr('a' + i); + inputs.write[i].name = String::chr('a' + i); if (from == 0) { - inputs[i].type = output_type; + inputs.write[i].type = output_type; } else { - inputs[i].type = inputs[from - 1].type; + inputs.write[i].type = inputs[from - 1].type; } } expression_dirty = true; @@ -78,10 +78,10 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val if (what == "type") { - inputs[idx].type = Variant::Type(int(p_value)); + inputs.write[idx].type = Variant::Type(int(p_value)); } else if (what == "name") { - inputs[idx].name = p_value; + inputs.write[idx].name = p_value; } else { return false; } @@ -1153,8 +1153,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { op->op = expression[i].op; op->nodes[0] = expression[i + 1].node; op->nodes[1] = NULL; - expression[i].is_op = false; - expression[i].node = op; + expression.write[i].is_op = false; + expression.write[i].node = op; expression.remove(i + 1); } @@ -1188,7 +1188,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() { op->nodes[1] = expression[next_op + 1].node; //next expression goes as right //replace all 3 nodes by this operator and make it an expression - expression[next_op - 1].node = op; + expression.write[next_op - 1].node = op; expression.remove(next_op); expression.remove(next_op); } @@ -1370,8 +1370,8 @@ public: bool ret = _execute(p_inputs, constructor->arguments[i], value, r_error_str, ce); if (ret) return true; - arr[i] = value; - argp[i] = &arr[i]; + arr.write[i] = value; + argp.write[i] = &arr[i]; } r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); @@ -1397,8 +1397,8 @@ public: bool ret = _execute(p_inputs, bifunc->arguments[i], value, r_error_str, ce); if (ret) return true; - arr[i] = value; - argp[i] = &arr[i]; + arr.write[i] = value; + argp.write[i] = &arr[i]; } VisualScriptBuiltinFunc::exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); @@ -1429,8 +1429,8 @@ public: bool ret = _execute(p_inputs, call->arguments[i], value, r_error_str, ce); if (ret) return true; - arr[i] = value; - argp[i] = &arr[i]; + arr.write[i] = value; + argp.write[i] = &arr[i]; } r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index ea23ab1b2a..7535f37ffc 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -684,7 +684,7 @@ bool VisualScriptSwitch::_set(const StringName &p_name, const Variant &p_value) int idx = String(p_name).get_slice("/", 1).to_int(); ERR_FAIL_INDEX_V(idx, case_values.size(), false); - case_values[idx].type = Variant::Type(int(p_value)); + case_values.write[idx].type = Variant::Type(int(p_value)); _change_notify(); ports_changed_notify(); @@ -767,7 +767,7 @@ PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, ""); + return PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_TYPE_STRING, get_base_type()); } String VisualScriptTypeCast::get_caption() const { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index bdf5705ecd..ad886bc758 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -43,7 +43,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return 0; else return 1; @@ -51,7 +51,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return false; else return true; @@ -231,7 +231,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con if (call_mode == CALL_MODE_INSTANCE) { if (p_idx == 0) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { p_idx--; } @@ -1055,7 +1055,7 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 8b7b809ec0..d499512d93 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -54,8 +54,8 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value arguments.resize(new_argc); for (int i = argc; i < new_argc; i++) { - arguments[i].name = "arg" + itos(i + 1); - arguments[i].type = Variant::NIL; + arguments.write[i].name = "arg" + itos(i + 1); + arguments.write[i].type = Variant::NIL; } ports_changed_notify(); _change_notify(); @@ -68,7 +68,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value if (what == "type") { Variant::Type new_type = Variant::Type(int(p_value)); - arguments[idx].type = new_type; + arguments.write[idx].type = new_type; ports_changed_notify(); return true; @@ -76,7 +76,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value if (what == "name") { - arguments[idx].name = p_value; + arguments.write[idx].name = p_value; ports_changed_notify(); return true; } @@ -205,6 +205,8 @@ PropertyInfo VisualScriptFunction::get_output_value_port_info(int p_idx) const { PropertyInfo out; out.type = arguments[p_idx].type; out.name = arguments[p_idx].name; + out.hint = arguments[p_idx].hint; + out.hint_string = arguments[p_idx].hint_string; return out; } @@ -218,11 +220,13 @@ String VisualScriptFunction::get_text() const { return get_name(); //use name as function name I guess } -void VisualScriptFunction::add_argument(Variant::Type p_type, const String &p_name, int p_index) { +void VisualScriptFunction::add_argument(Variant::Type p_type, const String &p_name, int p_index, const PropertyHint p_hint, const String &p_hint_string) { Argument arg; arg.name = p_name; arg.type = p_type; + arg.hint = p_hint; + arg.hint_string = p_hint_string; if (p_index >= 0) arguments.insert(p_index, arg); else @@ -234,7 +238,7 @@ void VisualScriptFunction::set_argument_type(int p_argidx, Variant::Type p_type) ERR_FAIL_INDEX(p_argidx, arguments.size()); - arguments[p_argidx].type = p_type; + arguments.write[p_argidx].type = p_type; ports_changed_notify(); } Variant::Type VisualScriptFunction::get_argument_type(int p_argidx) const { @@ -246,7 +250,7 @@ void VisualScriptFunction::set_argument_name(int p_argidx, const String &p_name) ERR_FAIL_INDEX(p_argidx, arguments.size()); - arguments[p_argidx].name = p_name; + arguments.write[p_argidx].name = p_name; ports_changed_notify(); } String VisualScriptFunction::get_argument_name(int p_argidx) const { @@ -1659,7 +1663,7 @@ Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const { class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance { public: - int value; + Variant value; bool valid; //virtual int get_working_memory_size() const { return 0; } @@ -1678,7 +1682,7 @@ public: VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instance(VisualScriptInstance *p_instance) { VisualScriptNodeInstanceBasicTypeConstant *instance = memnew(VisualScriptNodeInstanceBasicTypeConstant); - instance->value = Variant::get_numeric_constant_value(type, name, &instance->valid); + instance->value = Variant::get_constant_value(type, name, &instance->valid); return instance; } @@ -1687,7 +1691,7 @@ void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) c if (property.name == "constant") { List<StringName> constants; - Variant::get_numeric_constants_for_type(type, &constants); + Variant::get_constants_for_type(type, &constants); if (constants.size() == 0) { property.usage = 0; @@ -2198,7 +2202,7 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, "Scene Tree"); + return PropertyInfo(Variant::OBJECT, "Scene Tree", PROPERTY_HINT_TYPE_STRING, "SceneTree"); } String VisualScriptSceneTree::get_caption() const { @@ -3560,8 +3564,8 @@ void VisualScriptDeconstruct::_set_elem_cache(const Array &p_elements) { ERR_FAIL_COND(p_elements.size() % 2 == 1); elements.resize(p_elements.size() / 2); for (int i = 0; i < elements.size(); i++) { - elements[i].name = p_elements[i * 2 + 0]; - elements[i].type = Variant::Type(int(p_elements[i * 2 + 1])); + elements.write[i].name = p_elements[i * 2 + 0]; + elements.write[i].type = Variant::Type(int(p_elements[i * 2 + 1])); } } @@ -3606,7 +3610,7 @@ VisualScriptNodeInstance *VisualScriptDeconstruct::instance(VisualScriptInstance instance->instance = p_instance; instance->outputs.resize(elements.size()); for (int i = 0; i < elements.size(); i++) { - instance->outputs[i] = elements[i].name; + instance->outputs.write[i] = elements[i].name; } return instance; diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index 9bfbd46e47..f7ac995816 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -40,6 +40,8 @@ class VisualScriptFunction : public VisualScriptNode { struct Argument { String name; Variant::Type type; + PropertyHint hint; + String hint_string; }; Vector<Argument> arguments; @@ -70,7 +72,7 @@ public: virtual String get_text() const; virtual String get_category() const { return "flow_control"; } - void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1); + void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String("")); void set_argument_type(int p_argidx, Variant::Type p_type); Variant::Type get_argument_type(int p_argidx) const; void set_argument_name(int p_argidx, const String &p_name); diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp new file mode 100644 index 0000000000..e4dfc5fe45 --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -0,0 +1,734 @@ +/*************************************************************************/ +/* visual_script_property_selector.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "visual_script_property_selector.h" + +#include "editor_scale.h" +#include "modules/visual_script/visual_script.h" +#include "modules/visual_script/visual_script_builtin_funcs.h" +#include "modules/visual_script/visual_script_flow_control.h" +#include "modules/visual_script/visual_script_func_nodes.h" +#include "modules/visual_script/visual_script_nodes.h" +#include "os/keyboard.h" +#include "scene/main/node.h" +#include "scene/main/viewport.h" + +void VisualScriptPropertySelector::_text_changed(const String &p_newtext) { + _update_search(); +} + +void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { + + Ref<InputEventKey> k = p_ie; + + if (k.is_valid()) { + + switch (k->get_scancode()) { + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: { + + search_options->call("_gui_input", k); + search_box->accept_event(); + + TreeItem *root = search_options->get_root(); + if (!root->get_children()) + break; + + TreeItem *current = search_options->get_selected(); + + TreeItem *item = search_options->get_next_selected(root); + while (item) { + item->deselect(0); + item = search_options->get_next_selected(item); + } + + current->select(0); + + } break; + } + } +} + +void VisualScriptPropertySelector::_update_search() { + set_title(TTR("Search VisualScript")); + + search_options->clear(); + help_bit->set_text(""); + + TreeItem *root = search_options->create_item(); + bool found = false; + + if (properties) { + + List<PropertyInfo> props; + + if (instance) { + instance->get_property_list(&props, true); + } else if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + + v.get_property_list(&props); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + Object::cast_to<Script>(obj)->get_script_property_list(&props); + } + + StringName base = base_type; + while (base) { + props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + ClassDB::get_property_list(base, &props, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + + TreeItem *category = NULL; + + Ref<Texture> type_icons[Variant::VARIANT_MAX] = { + Control::get_icon("Variant", "EditorIcons"), + Control::get_icon("bool", "EditorIcons"), + Control::get_icon("int", "EditorIcons"), + Control::get_icon("float", "EditorIcons"), + Control::get_icon("String", "EditorIcons"), + Control::get_icon("Vector2", "EditorIcons"), + Control::get_icon("Rect2", "EditorIcons"), + Control::get_icon("Vector3", "EditorIcons"), + Control::get_icon("Transform2D", "EditorIcons"), + Control::get_icon("Plane", "EditorIcons"), + Control::get_icon("Quat", "EditorIcons"), + Control::get_icon("AABB", "EditorIcons"), + Control::get_icon("Basis", "EditorIcons"), + Control::get_icon("Transform", "EditorIcons"), + Control::get_icon("Color", "EditorIcons"), + Control::get_icon("Path", "EditorIcons"), + Control::get_icon("RID", "EditorIcons"), + Control::get_icon("Object", "EditorIcons"), + Control::get_icon("Dictionary", "EditorIcons"), + Control::get_icon("Array", "EditorIcons"), + Control::get_icon("PoolByteArray", "EditorIcons"), + Control::get_icon("PoolIntArray", "EditorIcons"), + Control::get_icon("PoolRealArray", "EditorIcons"), + Control::get_icon("PoolStringArray", "EditorIcons"), + Control::get_icon("PoolVector2Array", "EditorIcons"), + Control::get_icon("PoolVector3Array", "EditorIcons"), + Control::get_icon("PoolColorArray", "EditorIcons") + }; + + if (!seq_connect && visual_script_generic == false) { + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); + get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); + if (type == Variant::INT) { + get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL) { + get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) { + get_visual_node_names("operators/math/", Set<String>(), found, root, search_box); + } + } + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_CATEGORY) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name); + category->set_selectable(0, false); + + Ref<Texture> icon; + if (E->get().name == "Script Variables") { + icon = get_icon("Script", "EditorIcons"); + } else if (has_icon(E->get().name, "EditorIcons")) { + icon = get_icon(E->get().name, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + continue; + } + + if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) + continue; + + if (type_filter.size() && type_filter.find(E->get().type) == -1) + continue; + + String get_text_raw = String(TTR("Get")) + String(" ") + E->get().name; + String get_text = get_text_raw.capitalize(); + + String set_text_raw = String(TTR("Set ")) + String(" ") + E->get().name; + String set_text = set_text_raw.capitalize(); + String input = search_box->get_text().capitalize(); + if (input == String() || + get_text_raw.findn(input) != -1 || + get_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, get_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "get"); + item->set_collapsed(1); + item->set_selectable(0, true); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } + + if (input == String() || + set_text_raw.findn(input) != -1 && + set_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, set_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "set"); + item->set_selectable(0, true); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } + } + + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + } + + if (seq_connect == true && visual_script_generic == false) { + String text = search_box->get_text(); + create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition")); + create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch")); + create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence")); + create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator")); + create_visualscript_item(String("VisualScriptWhile"), root, text, String("While")); + create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return")); + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + } + + if (visual_script_generic) { + get_visual_node_names("", Set<String>(), found, root, search_box); + } + + List<MethodInfo> methods; + + if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + v.get_method_list(&methods); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + methods.push_back(MethodInfo("*Script Methods")); + Object::cast_to<Script>(obj)->get_script_method_list(&methods); + } + + StringName base = base_type; + while (base) { + methods.push_back(MethodInfo("*" + String(base))); + ClassDB::get_method_list(base, &methods, true, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + TreeItem *category = NULL; + bool script_methods = false; + + for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().name.begins_with("*")) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name.replace_first("*", "")); + category->set_selectable(0, false); + + Ref<Texture> icon; + script_methods = false; + print_line("name: " + E->get().name); + String rep = E->get().name.replace("*", ""); + if (E->get().name == "*Script Methods") { + icon = get_icon("Script", "EditorIcons"); + script_methods = true; + } else if (has_icon(rep, "EditorIcons")) { + icon = get_icon(rep, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + + continue; + } + + String name = E->get().name.get_slice(":", 0); + if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + MethodInfo mi = E->get(); + String desc = mi.name.capitalize() + " ("; + + if (search_box->get_text() != String() && + name.findn(search_box->get_text()) == -1 && + desc.findn(search_box->get_text()) == -1) + continue; + + TreeItem *item = search_options->create_item(category ? category : root); + + for (int i = 0; i < mi.arguments.size(); i++) { + + if (i > 0) + desc += ", "; + + if (mi.arguments[i].type == Variant::NIL) + desc += "var"; + else if (mi.arguments[i].name.find(":") != -1) { + desc += mi.arguments[i].name.get_slice(":", 1); + mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); + } else + desc += Variant::get_type_name(mi.arguments[i].type); + } + + desc += ")"; + + item->set_text(0, desc); + item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); + item->set_metadata(0, name); + item->set_selectable(0, true); + + item->set_metadata(1, "method"); + item->set_collapsed(1); + item->set_selectable(1, false); + + item->set_selectable(2, false); + item->set_metadata(2, connecting); + + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + } + + TreeItem *selected_item = search_options->search_item_text(search_box->get_text()); + if (!found && selected_item != NULL) { + selected_item->select(0); + found = true; + } + + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + + get_ok()->set_disabled(root->get_children() == NULL); +} + +void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) { + if (search_input == String() || text.findn(search_input) != -1) { + TreeItem *item = search_options->create_item(root); + item->set_text(0, text); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_metadata(0, name); + item->set_metadata(1, "action"); + item->set_selectable(0, true); + item->set_collapsed(1); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } +} + +void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) { + Map<String, TreeItem *> path_cache; + + List<String> fnodes; + VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); + + for (List<String>::Element *E = fnodes.front(); E; E = E->next()) { + if (!E->get().begins_with(root_filter)) { + continue; + } + Vector<String> path = E->get().split("/"); + bool is_filter = false; + for (Set<String>::Element *E = filter.front(); E; E = E->next()) { + if (path.size() >= 2 && path[1].findn(E->get()) != -1) { + is_filter = true; + break; + } + } + if (is_filter == true) { + continue; + } + + if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) { + continue; + } + TreeItem *item = search_options->create_item(root); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + String type_name; + if (vnode_operator != NULL) { + String type; + if (path.size() >= 2) { + type = path[1]; + } + type_name = type.capitalize() + " "; + } + VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_function_call != NULL) { + String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type()); + type_name = basic_type.capitalize() + " "; + } + VisualScriptBuiltinFunc *vnode_builtin_function_call = Object::cast_to<VisualScriptBuiltinFunc>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_builtin_function_call != NULL) { + type_name = "Builtin "; + } + item->set_text(0, type_name + path[path.size() - 1].capitalize()); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_selectable(0, true); + item->set_metadata(0, E->get()); + item->set_selectable(0, true); + item->set_metadata(1, "visualscript"); + item->set_selectable(1, false); + item->set_selectable(2, false); + item->set_metadata(2, connecting); + } +} + +void VisualScriptPropertySelector::_confirmed() { + + TreeItem *ti = search_options->get_selected(); + if (!ti) + return; + emit_signal("selected", ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2)); + hide(); +} + +void VisualScriptPropertySelector::_item_selected() { + + help_bit->set_text(""); + + TreeItem *item = search_options->get_selected(); + if (!item) + return; + String name = item->get_metadata(0); + + String class_type; + if (type) { + class_type = Variant::get_type_name(type); + + } else { + class_type = base_type; + } + + DocData *dd = EditorHelp::get_doc_data(); + String text; + + String at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().properties.size(); i++) { + if (E->get().properties[i].name == name) { + text = E->get().properties[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + if (E->get().methods[i].name == name) { + text = E->get().methods[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + Vector<String> functions = name.rsplit("/", false, 1); + if (E->get().methods[i].name == functions[functions.size() - 1]) { + text = E->get().methods[i].description; + } + } + } + + List<String> *names = memnew(List<String>); + VisualScriptLanguage::singleton->get_registered_node_names(names); + if (names->find(name) != NULL) { + Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (operator_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(operator_node->get_class_name()); + if (E) { + text = Variant::get_operator_name(operator_node->get_operator()); + } + } + Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (typecast_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(typecast_node->get_class_name()); + if (E) { + text = E->get().description; + } + } + + Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (builtin_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(builtin_node->get_class_name()); + if (E) { + for (int i = 0; i < E->get().constants.size(); i++) { + if (E->get().constants[i].value.to_int() == int(builtin_node->get_func())) { + text = E->get().constants[i].description; + } + } + } + } + } + + memdelete(names); + + if (text == String()) + return; + + help_bit->set_text(text); +} + +void VisualScriptPropertySelector::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + connect("confirmed", this, "_confirmed"); + } +} + +void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) { + type_filter = p_type_filter; +} + +void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = p_seq_connect; + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting) { + ERR_FAIL_COND(p_script.is_null()); + + base_type = p_script->get_instance_base_type(); + selected = p_current; + type = Variant::NIL; + script = p_script->get_instance_id(); + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting) { + ERR_FAIL_COND(p_type == Variant::NIL); + base_type = ""; + selected = p_current; + type = p_type; + script = 0; + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting) { + base_type = p_type; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = true; + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting) { + base_type = ""; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = p_instance; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting) { + base_type = p_base; + selected = ""; + type = Variant::NIL; + script = 0; + properties = true; + visual_script_generic = true; + instance = NULL; + virtuals_only = false; + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + connecting = p_connecting; + + _update_search(); +} + +void VisualScriptPropertySelector::show_window(float p_screen_ratio) { + Rect2 rect; + Point2 window_size = get_viewport_rect().size; + rect.size = (window_size * p_screen_ratio).floor(); + rect.size.x = rect.size.x / 1.25f; + rect.position = ((window_size - rect.size) / 2.0f).floor(); + popup(rect); +} + +void VisualScriptPropertySelector::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_text_changed"), &VisualScriptPropertySelector::_text_changed); + ClassDB::bind_method(D_METHOD("_confirmed"), &VisualScriptPropertySelector::_confirmed); + ClassDB::bind_method(D_METHOD("_sbox_input"), &VisualScriptPropertySelector::_sbox_input); + ClassDB::bind_method(D_METHOD("_item_selected"), &VisualScriptPropertySelector::_item_selected); + + ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting"))); +} + +VisualScriptPropertySelector::VisualScriptPropertySelector() { + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + //set_child_rect(vbc); + search_box = memnew(LineEdit); + vbc->add_margin_child(TTR("Search:"), search_box); + search_box->connect("text_changed", this, "_text_changed"); + search_box->connect("gui_input", this, "_sbox_input"); + search_options = memnew(Tree); + vbc->add_margin_child(TTR("Matches:"), search_options, true); + get_ok()->set_text(TTR("Open")); + get_ok()->set_disabled(true); + register_text_enter(search_box); + set_hide_on_ok(false); + search_options->connect("item_activated", this, "_confirmed"); + search_options->connect("cell_selected", this, "_item_selected"); + search_options->set_hide_root(true); + search_options->set_hide_folding(true); + virtuals_only = false; + help_bit = memnew(EditorHelpBit); + vbc->add_margin_child(TTR("Description:"), help_bit); + help_bit->connect("request_hide", this, "_closed"); + search_options->set_columns(3); + search_options->set_column_expand(1, false); + search_options->set_column_expand(2, false); +} diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h new file mode 100644 index 0000000000..917ef9ae6d --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* visual_script_property_selector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H +#define VISUALSCRIPT_PROPERTYSELECTOR_H + +#include "editor/property_editor.h" +#include "editor_help.h" +#include "scene/gui/rich_text_label.h" + +class VisualScriptPropertySelector : public ConfirmationDialog { + GDCLASS(VisualScriptPropertySelector, ConfirmationDialog) + + LineEdit *search_box; + Tree *search_options; + + void _update_search(); + + void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text); + + void get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box); + + void _sbox_input(const Ref<InputEvent> &p_ie); + + void _confirmed(); + void _text_changed(const String &p_newtext); + + EditorHelpBit *help_bit; + + bool properties; + bool visual_script_generic; + bool connecting; + String selected; + Variant::Type type; + String base_type; + ObjectID script; + Object *instance; + bool virtuals_only; + + bool seq_connect = false; + + void _item_selected(); + + Vector<Variant::Type> type_filter; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true); + void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true); + void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true); + void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true); + void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true); + void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true); + void select_from_visual_script(const String &p_base, const bool p_connecting = true); + + void show_window(float p_screen_ratio); + + void set_type_filter(const Vector<Variant::Type> &p_type_filter); + + VisualScriptPropertySelector(); +}; + +#endif // VISUALSCRIPT_PROPERTYSELECTOR_H diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 1405fa98b0..00c36ebb47 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,7 +64,6 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, String str = "ws://"; String proto_string = ""; - int i = 0; if (p_ssl) str = "wss://"; diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp index 06f97aaf05..ac31daa108 100644 --- a/modules/websocket/lws_client.cpp +++ b/modules/websocket/lws_client.cpp @@ -127,11 +127,6 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi case LWS_CALLBACK_CLIENT_ESTABLISHED: peer->set_wsi(wsi); peer_data->peer_id = 0; - peer_data->in_size = 0; - peer_data->in_count = 0; - peer_data->out_count = 0; - peer_data->rbw.resize(16); - peer_data->rbr.resize(16); peer_data->force_close = false; _on_connect(lws_get_protocol(wsi)->name); break; @@ -142,10 +137,6 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi return -1; // we should close the connection (would probably happen anyway) case LWS_CALLBACK_CLIENT_CLOSED: - peer_data->in_count = 0; - peer_data->out_count = 0; - peer_data->rbw.resize(0); - peer_data->rbr.resize(0); peer->close(); destroy_context(); _on_disconnect(); diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 96acb99cc4..0989357258 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -41,6 +41,10 @@ #include "drivers/unix/socket_helpers.h" void LWSPeer::set_wsi(struct lws *p_wsi) { + ERR_FAIL_COND(wsi != NULL); + + rbw.resize(16); + rbr.resize(16); wsi = p_wsi; }; @@ -57,24 +61,24 @@ Error LWSPeer::read_wsi(void *in, size_t len) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); - uint32_t size = peer_data->in_size; + uint32_t size = in_size; uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; - if (peer_data->rbr.space_left() < len + 5) { + if (rbr.space_left() < len + 5) { ERR_EXPLAIN("Buffer full! Dropping data"); ERR_FAIL_V(FAILED); } - copymem(&(peer_data->input_buffer[size]), in, len); + copymem(&(input_buffer[size]), in, len); size += len; - peer_data->in_size = size; + in_size = size; if (lws_is_final_fragment(wsi)) { - peer_data->rbr.write((uint8_t *)&size, 4); - peer_data->rbr.write((uint8_t *)&is_string, 1); - peer_data->rbr.write(peer_data->input_buffer, size); - peer_data->in_count++; - peer_data->in_size = 0; + rbr.write((uint8_t *)&size, 4); + rbr.write((uint8_t *)&is_string, 1); + rbr.write(input_buffer, size); + in_count++; + in_size = 0; } return OK; @@ -86,26 +90,26 @@ Error LWSPeer::write_wsi() { PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); PoolVector<uint8_t> tmp; - int left = peer_data->rbw.data_left(); + int left = rbw.data_left(); uint32_t to_write = 0; - if (left == 0 || peer_data->out_count == 0) + if (left == 0 || out_count == 0) return OK; - peer_data->rbw.read((uint8_t *)&to_write, 4); - peer_data->out_count--; + rbw.read((uint8_t *)&to_write, 4); + out_count--; if (left < to_write) { - peer_data->rbw.advance_read(left); + rbw.advance_read(left); return FAILED; } tmp.resize(LWS_PRE + to_write); - peer_data->rbw.read(&(tmp.write()[LWS_PRE]), to_write); + rbw.read(&(tmp.write()[LWS_PRE]), to_write); lws_write(wsi, &(tmp.write()[LWS_PRE]), to_write, (enum lws_write_protocol)write_mode); tmp.resize(0); - if (peer_data->out_count > 0) + if (out_count > 0) lws_callback_on_writable(wsi); // we want to write more! return OK; @@ -116,9 +120,9 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); - peer_data->rbw.write((uint8_t *)&p_buffer_size, 4); - peer_data->rbw.write(p_buffer, MIN(p_buffer_size, peer_data->rbw.space_left())); - peer_data->out_count++; + rbw.write((uint8_t *)&p_buffer_size, 4); + rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left())); + out_count++; lws_callback_on_writable(wsi); // notify that we want to write return OK; @@ -130,7 +134,7 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); - if (peer_data->in_count == 0) + if (in_count == 0) return ERR_UNAVAILABLE; uint32_t to_read = 0; @@ -138,17 +142,17 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { uint8_t is_string = 0; r_buffer_size = 0; - peer_data->rbr.read((uint8_t *)&to_read, 4); - peer_data->in_count--; - left = peer_data->rbr.data_left(); + rbr.read((uint8_t *)&to_read, 4); + in_count--; + left = rbr.data_left(); if (left < to_read + 1) { - peer_data->rbr.advance_read(left); + rbr.advance_read(left); return FAILED; } - peer_data->rbr.read(&is_string, 1); - peer_data->rbr.read(packet_buffer, to_read); + rbr.read(&is_string, 1); + rbr.read(packet_buffer, to_read); *r_buffer = packet_buffer; r_buffer_size = to_read; _was_string = is_string; @@ -161,7 +165,7 @@ int LWSPeer::get_available_packet_count() const { if (!is_connected_to_host()) return 0; - return ((PeerData *)lws_wsi_user(wsi))->in_count; + return in_count; }; bool LWSPeer::was_string_packet() const { @@ -176,12 +180,17 @@ bool LWSPeer::is_connected_to_host() const { void LWSPeer::close() { if (wsi != NULL) { - struct lws *tmp = wsi; PeerData *data = ((PeerData *)lws_wsi_user(wsi)); data->force_close = true; - wsi = NULL; - lws_callback_on_writable(tmp); // notify that we want to disconnect + lws_callback_on_writable(wsi); // notify that we want to disconnect } + wsi = NULL; + rbw.resize(0); + rbr.resize(0); + in_count = 0; + in_size = 0; + out_count = 0; + _was_string = false; }; IP_Address LWSPeer::get_connected_host() const { @@ -228,8 +237,8 @@ uint16_t LWSPeer::get_connected_port() const { LWSPeer::LWSPeer() { wsi = NULL; - _was_string = false; write_mode = WRITE_MODE_BINARY; + close(); }; LWSPeer::~LWSPeer() { diff --git a/modules/websocket/lws_peer.h b/modules/websocket/lws_peer.h index e96b38b168..d7d46e3076 100644 --- a/modules/websocket/lws_peer.h +++ b/modules/websocket/lws_peer.h @@ -57,14 +57,15 @@ public: struct PeerData { uint32_t peer_id; bool force_close; - RingBuffer<uint8_t> rbw; - RingBuffer<uint8_t> rbr; - mutable uint8_t input_buffer[PACKET_BUFFER_SIZE]; - uint32_t in_size; - int in_count; - int out_count; }; + RingBuffer<uint8_t> rbw; + RingBuffer<uint8_t> rbr; + uint8_t input_buffer[PACKET_BUFFER_SIZE]; + uint32_t in_size; + int in_count; + int out_count; + virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp index 8d13dc7a98..bb724bce9c 100644 --- a/modules/websocket/lws_server.cpp +++ b/modules/websocket/lws_server.cpp @@ -92,11 +92,6 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi _peer_map[id] = peer; peer_data->peer_id = id; - peer_data->in_size = 0; - peer_data->in_count = 0; - peer_data->out_count = 0; - peer_data->rbw.resize(16); - peer_data->rbr.resize(16); peer_data->force_close = false; _on_connect(id, lws_get_protocol(wsi)->name); @@ -111,10 +106,6 @@ int LWSServer::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi _peer_map[id]->close(); _peer_map.erase(id); } - peer_data->in_count = 0; - peer_data->out_count = 0; - peer_data->rbr.resize(0); - peer_data->rbw.resize(0); _on_disconnect(id); return 0; // we can end here } diff --git a/platform/SCsub b/platform/SCsub index 2019a30be7..0f9c2047a0 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -18,10 +18,13 @@ for platform in env.platform_apis: reg_apis_inc += '\n' reg_apis += '}\n\n' unreg_apis += '}\n' + +# NOTE: It is safe to generate this file here, since this is still execute serially with open_utf8('register_platform_apis.gen.cpp', 'w') as f: - f.write(reg_apis_inc) - f.write(reg_apis) - f.write(unreg_apis) + f.write(reg_apis_inc) + f.write(reg_apis) + f.write(unreg_apis) + platform_sources.append('register_platform_apis.gen.cpp') lib = env.add_library('platform', platform_sources) diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index fd834193dd..28e3ea962f 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -146,9 +146,6 @@ void AudioDriverOpenSL::start() { res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void *)&EngineItf); ERR_FAIL_COND(res != SL_RESULT_SUCCESS); - /* Initialize arrays required[] and iidArray[] */ - SLboolean required[MAX_NUMBER_INTERFACES]; - SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; { const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; @@ -188,10 +185,7 @@ void AudioDriverOpenSL::start() { //cntxt.pDataBase = (void*)&pcmData; //cntxt.pData = cntxt.pDataBase; //cntxt.size = sizeof(pcmData); - /* Set arrays required[] and iidArray[] for SEEK interface - (PlayItf is implicit) */ - required[0] = SL_BOOLEAN_TRUE; - iidArray[0] = SL_IID_BUFFERQUEUE; + /* Create the music player */ { diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 3e40b59de9..ee5ae156b7 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -153,7 +153,6 @@ String DirAccessJAndroid::get_current_dir() { bool DirAccessJAndroid::file_exists(String p_file) { - JNIEnv *env = ThreadAndroid::get_env(); String sd; if (current_dir == "") sd = p_file; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index c562a47b00..9ad0219746 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -528,11 +528,9 @@ class EditorExportAndroid : public EditorExportPlatform { bool exported = false; for (int i = 0; i < p_so.tags.size(); ++i) { // shared objects can be fat (compatible with multiple ABIs) - int start_pos = 0; int abi_index = abis.find(p_so.tags[i]); if (abi_index != -1) { exported = true; - start_pos = abi_index + 1; String abi = abis[abi_index]; String dst_path = "lib/" + abi + "/" + p_so.path.get_file(); Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); @@ -667,11 +665,11 @@ class EditorExportAndroid : public EditorExportPlatform { ucstring.resize(len + 1); for (uint32_t j = 0; j < len; j++) { uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); - ucstring[j] = c; + ucstring.write[j] = c; } string_end = MAX(string_at + 2 + 2 * len, string_end); - ucstring[len] = 0; - string_table[i] = ucstring.ptr(); + ucstring.write[len] = 0; + string_table.write[i] = ucstring.ptr(); } //print_line("String "+itos(i)+": "+string_table[i]); @@ -718,13 +716,13 @@ class EditorExportAndroid : public EditorExportPlatform { if (tname == "manifest" && attrname == "package") { print_line("FOUND package"); - string_table[attr_value] = get_package_name(package_name); + string_table.write[attr_value] = get_package_name(package_name); } if (tname == "manifest" && /*nspace=="android" &&*/ attrname == "versionCode") { print_line("FOUND versionCode"); - encode_uint32(version_code, &p_manifest[iofs + 16]); + encode_uint32(version_code, &p_manifest.write[iofs + 16]); } if (tname == "manifest" && /*nspace=="android" &&*/ attrname == "versionName") { @@ -733,12 +731,12 @@ class EditorExportAndroid : public EditorExportPlatform { if (attr_value == 0xFFFFFFFF) { WARN_PRINT("Version name in a resource, should be plaintext") } else - string_table[attr_value] = version_name; + string_table.write[attr_value] = version_name; } if (tname == "activity" && /*nspace=="android" &&*/ attrname == "screenOrientation") { - encode_uint32(orientation == 0 ? 0 : 1, &p_manifest[iofs + 16]); + encode_uint32(orientation == 0 ? 0 : 1, &p_manifest.write[iofs + 16]); } if (tname == "uses-feature" && /*nspace=="android" &&*/ attrname == "glEsVersion") { @@ -749,19 +747,19 @@ class EditorExportAndroid : public EditorExportPlatform { if (attrname == "smallScreens") { - encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]); + encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "normalScreens") { - encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]); + encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "largeScreens") { - encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]); + encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } else if (attrname == "xlargeScreens") { - encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest[iofs + 16]); + encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); } } @@ -813,45 +811,45 @@ class EditorExportAndroid : public EditorExportPlatform { } // start tag - encode_uint16(0x102, &p_manifest[ofs]); // type - encode_uint16(16, &p_manifest[ofs + 2]); // headersize - encode_uint32(56, &p_manifest[ofs + 4]); // size - encode_uint32(0, &p_manifest[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest[ofs + 12]); // comment - encode_uint32(-1, &p_manifest[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name - encode_uint16(20, &p_manifest[ofs + 24]); // attr_start - encode_uint16(20, &p_manifest[ofs + 26]); // attr_size - encode_uint16(1, &p_manifest[ofs + 28]); // num_attrs - encode_uint16(0, &p_manifest[ofs + 30]); // id_index - encode_uint16(0, &p_manifest[ofs + 32]); // class_index - encode_uint16(0, &p_manifest[ofs + 34]); // style_index + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(56, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index // attribute - encode_uint32(ns_android_string, &p_manifest[ofs + 36]); // ns - encode_uint32(attr_name_string, &p_manifest[ofs + 40]); // 'name' - encode_uint32(perm_string, &p_manifest[ofs + 44]); // raw_value - encode_uint16(8, &p_manifest[ofs + 48]); // typedvalue_size - p_manifest[ofs + 50] = 0; // typedvalue_always0 - p_manifest[ofs + 51] = 0x03; // typedvalue_type (string) - encode_uint32(perm_string, &p_manifest[ofs + 52]); // typedvalue reference + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference ofs += 56; // end tag - encode_uint16(0x103, &p_manifest[ofs]); // type - encode_uint16(16, &p_manifest[ofs + 2]); // headersize - encode_uint32(24, &p_manifest[ofs + 4]); // size - encode_uint32(0, &p_manifest[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest[ofs + 12]); // comment - encode_uint32(-1, &p_manifest[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name ofs += 24; } // copy footer back in - memcpy(&p_manifest[ofs], manifest_end.ptr(), manifest_end.size()); + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); } } break; } @@ -866,19 +864,19 @@ class EditorExportAndroid : public EditorExportPlatform { for (uint32_t i = 0; i < string_table_begins; i++) { - ret[i] = p_manifest[i]; + ret.write[i] = p_manifest[i]; } ofs = 0; for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret[string_table_begins + i * 4]); + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); ofs += string_table[i].length() * 2 + 2 + 2; } ret.resize(ret.size() + ofs); string_data_offset = ret.size() - ofs; - uint8_t *chars = &ret[string_data_offset]; + uint8_t *chars = &ret.write[string_data_offset]; for (int i = 0; i < string_table.size(); i++) { String s = string_table[i]; @@ -905,15 +903,15 @@ class EditorExportAndroid : public EditorExportPlatform { uint32_t extra = (p_manifest.size() - string_table_ends); ret.resize(new_stable_end + extra); for (uint32_t i = 0; i < extra; i++) - ret[new_stable_end + i] = p_manifest[string_table_ends + i]; + ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; while (ret.size() % 4) ret.push_back(0); - encode_uint32(ret.size(), &ret[4]); //update new file size + encode_uint32(ret.size(), &ret.write[4]); //update new file size - encode_uint32(new_stable_end - 8, &ret[12]); //update new string table size - encode_uint32(string_table.size(), &ret[16]); //update new number of strings - encode_uint32(string_data_offset - 8, &ret[28]); //update new string data offset + encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size + encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings + encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset //print_line("file size: "+itos(ret.size())); @@ -937,9 +935,9 @@ class EditorExportAndroid : public EditorExportPlatform { Vector<uint8_t> str8; str8.resize(len + 1); for (uint32_t i = 0; i < len; i++) { - str8[i] = p_bytes[offset + i]; + str8.write[i] = p_bytes[offset + i]; } - str8[len] = 0; + str8.write[len] = 0; String str; str.parse_utf8((const char *)str8.ptr()); return str; @@ -1003,18 +1001,18 @@ class EditorExportAndroid : public EditorExportPlatform { for (uint32_t i = 0; i < string_table_begins; i++) { - ret[i] = p_manifest[i]; + ret.write[i] = p_manifest[i]; } int ofs = 0; for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret[string_table_begins + i * 4]); + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); ofs += string_table[i].length() * 2 + 2 + 2; } ret.resize(ret.size() + ofs); - uint8_t *chars = &ret[ret.size() - ofs]; + uint8_t *chars = &ret.write[ret.size() - ofs]; for (int i = 0; i < string_table.size(); i++) { String s = string_table[i]; @@ -1033,19 +1031,19 @@ class EditorExportAndroid : public EditorExportPlatform { ret.push_back(0); //change flags to not use utf8 - encode_uint32(string_flags & ~0x100, &ret[28]); + encode_uint32(string_flags & ~0x100, &ret.write[28]); //change length - encode_uint32(ret.size() - 12, &ret[16]); + encode_uint32(ret.size() - 12, &ret.write[16]); //append the rest... int rest_from = 12 + string_block_len; int rest_to = ret.size(); int rest_len = (p_manifest.size() - rest_from); ret.resize(ret.size() + (p_manifest.size() - rest_from)); for (int i = 0; i < rest_len; i++) { - ret[rest_to + i] = p_manifest[rest_from + i]; + ret.write[rest_to + i] = p_manifest[rest_from + i]; } //finally update the size - encode_uint32(ret.size(), &ret[4]); + encode_uint32(ret.size(), &ret.write[4]); p_manifest = ret; //printf("end\n"); @@ -1646,7 +1644,7 @@ public: //add comandline Vector<uint8_t> clf; clf.resize(4); - encode_uint32(cl.size(), &clf[0]); + encode_uint32(cl.size(), &clf.write[0]); for (int i = 0; i < cl.size(); i++) { print_line(itos(i) + " param: " + cl[i]); @@ -1656,8 +1654,8 @@ public: if (!length) continue; clf.resize(base + 4 + length); - encode_uint32(length, &clf[base]); - copymem(&clf[base + 4], txt.ptr(), length); + encode_uint32(length, &clf.write[base]); + copymem(&clf.write[base + 4], txt.ptr(), length); } zip_fileinfo zipfi = get_zip_fileinfo(); diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index e6240ad9e9..8bb1c38345 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -880,7 +880,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo const char **cmdline = NULL; int cmdlen = 0; - bool use_apk_expansion = false; if (p_cmdline) { cmdlen = env->GetArrayLength(p_cmdline); if (cmdlen) { @@ -891,9 +890,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); const char *rawString = env->GetStringUTFChars(string, 0); - if (rawString && strcmp(rawString, "--main-pack") == 0) { - use_apk_expansion = true; - } cmdline[i] = rawString; } diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index cc512263bc..f9eda9dff1 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -357,8 +357,8 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> touch.resize(p_points.size()); for (int i = 0; i < p_points.size(); i++) { - touch[i].id = p_points[i].id; - touch[i].pos = p_points[i].pos; + touch.write[i].id = p_points[i].id; + touch.write[i].pos = p_points[i].pos; } //send touch @@ -399,7 +399,7 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> ev->set_position(p_points[idx].pos); ev->set_relative(p_points[idx].pos - touch[i].pos); input->parse_input_event(ev); - touch[i].pos = p_points[idx].pos; + touch.write[i].pos = p_points[idx].pos; } } break; diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index e85813605f..b13baf69c2 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -132,7 +132,7 @@ JNIEnv *ThreadAndroid::get_env() { } JNIEnv *env = NULL; - int status = java_vm->AttachCurrentThread(&env, NULL); + java_vm->AttachCurrentThread(&env, NULL); return env; } diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 4c1e02baf7..e59c81a148 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -276,7 +276,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ CharString cs = strnew.utf8(); pfile.resize(cs.size() - 1); for (int i = 0; i < cs.size() - 1; i++) { - pfile[i] = cs[i]; + pfile.write[i] = cs[i]; } } @@ -489,7 +489,7 @@ private: ret.resize(sizeof(num) * 2); for (int i = 0; i < sizeof(num) * 2; ++i) { uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; - ret[i] = _hex_char(four_bits); + ret.write[i] = _hex_char(four_bits); } return String::utf8(ret.ptr(), ret.size()); } @@ -587,7 +587,7 @@ void EditorExportPlatformIOS::_add_assets_to_project(Vector<uint8_t> &p_project_ CharString cs = str.utf8(); p_project_data.resize(cs.size() - 1); for (int i = 0; i < cs.size() - 1; i++) { - p_project_data[i] = cs[i]; + p_project_data.write[i] = cs[i]; } } @@ -781,7 +781,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p int ret = unzGoToFirstFile(src_pkg_zip); Vector<uint8_t> project_file_data; while (ret == UNZ_OK) { +#if defined(OSX_ENABLED) || defined(X11_ENABLED) bool is_execute = false; +#endif //get filename unz_file_info info; @@ -812,7 +814,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p continue; //ignore! } found_library = true; +#if defined(OSX_ENABLED) || defined(X11_ENABLED) is_execute = true; +#endif file = "godot_ios.a"; } if (file == project_file) { @@ -855,7 +859,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p f->close(); memdelete(f); -#ifdef OSX_ENABLED +#if defined(OSX_ENABLED) || defined(X11_ENABLED) if (is_execute) { // we need execute rights on this file chmod(file.utf8().get_data(), 0755); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 9591850662..7cff6ba172 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -95,7 +95,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re CharString cs = str_export.utf8(); p_html.resize(cs.length()); for (int i = 0; i < cs.length(); i++) { - p_html[i] = cs[i]; + p_html.write[i] = cs[i]; } } diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 4dfa46528a..5c973c30c2 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -3,14 +3,8 @@ import os Import('env') -def make_debug(target, source, env): - if (env["macports_clang"] != 'no'): - mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") - mpclangver = env["macports_clang"] - os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil {0} -o {0}.dSYM'.format(target[0])) - else: - os.system('dsymutil {0} -o {0}.dSYM'.format(target[0])) - os.system('strip -u -r {0}'.format(target[0])) +from platform_methods import run_in_subprocess +import platform_osx_builders files = [ 'crash_handler_osx.mm', @@ -25,5 +19,5 @@ files = [ prog = env.add_program('#bin/godot', files) if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: - env.AddPostAction(prog, make_debug) + env.AddPostAction(prog, run_in_subprocess(platform_osx_builders.make_debug_osx)) diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 1cc5c619fe..8a0883eca3 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -115,8 +115,8 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/osx']) - env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) - env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) env.Append(LIBS=['pthread']) env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index db265812fa..b630e4f223 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -137,10 +137,10 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_ Vector<uint8_t> data; data.resize(8); - data[0] = 'i'; - data[1] = 'c'; - data[2] = 'n'; - data[3] = 's'; + data.write[0] = 'i'; + data.write[1] = 'c'; + data.write[2] = 'n'; + data.write[3] = 's'; const char *name[] = { "ic09", "ic08", "ic07", "icp6", "icp5", "icp4" }; int index = 0; @@ -160,19 +160,19 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_ int ofs = data.size(); uint32_t len = f->get_len(); data.resize(data.size() + len + 8); - f->get_buffer(&data[ofs + 8], len); + f->get_buffer(&data.write[ofs + 8], len); memdelete(f); len += 8; len = BSWAP32(len); - copymem(&data[ofs], name[index], 4); - encode_uint32(len, &data[ofs + 4]); + copymem(&data.write[ofs], name[index], 4); + encode_uint32(len, &data.write[ofs + 4]); index++; size /= 2; } uint32_t total_len = data.size(); total_len = BSWAP32(total_len); - encode_uint32(total_len, &data[4]); + encode_uint32(total_len, &data.write[4]); p_data = data; } @@ -210,7 +210,7 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset CharString cs = strnew.utf8(); plist.resize(cs.size() - 1); for (int i = 0; i < cs.size() - 1; i++) { - plist[i] = cs[i]; + plist.write[i] = cs[i]; } } diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index 20ceadca9d..365b39c573 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -271,7 +271,7 @@ void JoypadOSX::_device_removed(int p_id) { ERR_FAIL_COND(device == -1); input->joy_connection_changed(p_id, false, ""); - device_list[device].free(); + device_list.write[device].free(); device_list.remove(device); } @@ -460,19 +460,19 @@ void JoypadOSX::process_joypads() { poll_joypads(); for (int i = 0; i < device_list.size(); i++) { - joypad &joy = device_list[i]; + joypad &joy = device_list.write[i]; for (int j = 0; j < joy.axis_elements.size(); j++) { - rec_element &elem = joy.axis_elements[j]; + rec_element &elem = joy.axis_elements.write[j]; int value = joy.get_hid_element_state(&elem); input->joy_axis(joy.id, j, axis_correct(value, elem.min, elem.max)); } for (int j = 0; j < joy.button_elements.size(); j++) { - int value = joy.get_hid_element_state(&joy.button_elements[j]); + int value = joy.get_hid_element_state(&joy.button_elements.write[j]); input->joy_button(joy.id, j, (value >= 1)); } for (int j = 0; j < joy.hat_elements.size(); j++) { - rec_element &elem = joy.hat_elements[j]; + rec_element &elem = joy.hat_elements.write[j]; int value = joy.get_hid_element_state(&elem); int hat_value = process_hat_value(elem.min, elem.max, value); input->joy_hat(joy.id, hat_value); @@ -495,7 +495,7 @@ void JoypadOSX::process_joypads() { } void JoypadOSX::joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp) { - joypad *joy = &device_list[get_joy_index(p_id)]; + joypad *joy = &device_list.write[get_joy_index(p_id)]; joy->ff_timestamp = p_timestamp; joy->ff_effect.dwDuration = p_duration * FF_SECONDS; joy->ff_effect.dwGain = p_magnitude * FF_FFNOMINALMAX; @@ -504,7 +504,7 @@ void JoypadOSX::joypad_vibration_start(int p_id, float p_magnitude, float p_dura } void JoypadOSX::joypad_vibration_stop(int p_id, uint64_t p_timestamp) { - joypad *joy = &device_list[get_joy_index(p_id)]; + joypad *joy = &device_list.write[get_joy_index(p_id)]; joy->ff_timestamp = p_timestamp; FFEffectStop(joy->ff_object); } @@ -596,7 +596,7 @@ JoypadOSX::JoypadOSX() { JoypadOSX::~JoypadOSX() { for (int i = 0; i < device_list.size(); i++) { - device_list[i].free(); + device_list.write[i].free(); } IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE); diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 3d14a6d4fb..686e3f8c90 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -33,6 +33,7 @@ #include "crash_handler_osx.h" #include "drivers/coreaudio/audio_driver_coreaudio.h" +#include "drivers/coremidi/core_midi.h" #include "drivers/unix/os_unix.h" #include "joypad_osx.h" #include "main/input_default.h" @@ -74,6 +75,7 @@ public: IP_Unix *ip_unix; AudioDriverCoreAudio audio_driver; + MIDIDriverCoreMidi midi_driver; InputDefault *input; JoypadOSX *joypad_osx; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 282f5e2d1b..7bf274310d 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -95,7 +95,7 @@ static void push_to_key_event_buffer(const OS_OSX::KeyEvent &p_event) { if (OS_OSX::singleton->key_event_pos >= buffer.size()) { buffer.resize(1 + OS_OSX::singleton->key_event_pos); } - buffer[OS_OSX::singleton->key_event_pos++] = p_event; + buffer.write[OS_OSX::singleton->key_event_pos++] = p_event; } static int mouse_x = 0; @@ -602,8 +602,8 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { mm->set_position(pos); mm->set_global_position(pos); Vector2 relativeMotion = Vector2(); - relativeMotion.x = [event deltaX] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - relativeMotion.y = [event deltaY] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale([[event window] backingScaleFactor]); + relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale([[event window] backingScaleFactor]); mm->set_relative(relativeMotion); get_key_modifier_state([event modifierFlags], mm); @@ -1351,6 +1351,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); + midi_driver.open(); + input = memnew(InputDefault); joypad_osx = memnew(JoypadOSX); @@ -2403,7 +2405,7 @@ void OS_OSX::process_key_events() { Ref<InputEventKey> k; for (int i = 0; i < key_event_pos; i++) { - KeyEvent &ke = key_event_buffer[i]; + const KeyEvent &ke = key_event_buffer[i]; if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { k.instance(); diff --git a/platform/osx/platform_osx_builders.py b/platform/osx/platform_osx_builders.py new file mode 100644 index 0000000000..81997f674b --- /dev/null +++ b/platform/osx/platform_osx_builders.py @@ -0,0 +1,21 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +from platform_methods import subprocess_main + + +def make_debug_osx(target, source, env): + if (env["macports_clang"] != 'no'): + mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") + mpclangver = env["macports_clang"] + os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil {0} -o {0}.dSYM'.format(target[0])) + else: + os.system('dsymutil {0} -o {0}.dSYM'.format(target[0])) + os.system('strip -u -r {0}'.format(target[0])) + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 35c0b30ce4..ebc2c2d7a2 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -301,37 +301,37 @@ Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { int offs = 0; // Write magic - offs += buf_put_int32(FILE_HEADER_MAGIC, &buf[offs]); + offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); // Version - offs += buf_put_int16(ZIP_VERSION, &buf[offs]); + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); // Special flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf[offs]); + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); // Compression - offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf[offs]); + offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); // File date and time - offs += buf_put_int32(0, &buf[offs]); + offs += buf_put_int32(0, &buf.write[offs]); // CRC-32 - offs += buf_put_int32(p_file_meta.file_crc32, &buf[offs]); + offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); // Compressed size - offs += buf_put_int32(p_file_meta.compressed_size, &buf[offs]); + offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); // Uncompressed size - offs += buf_put_int32(p_file_meta.uncompressed_size, &buf[offs]); + offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); // File name length - offs += buf_put_int16(p_file_meta.name.length(), &buf[offs]); + offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); // Extra data length - offs += buf_put_int16(0, &buf[offs]); + offs += buf_put_int16(0, &buf.write[offs]); // File name - offs += buf_put_string(p_file_meta.name, &buf[offs]); + offs += buf_put_string(p_file_meta.name, &buf.write[offs]); // Done! return buf; @@ -344,47 +344,47 @@ void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_ha buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); // Write magic - offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf[offs]); + offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); // ZIP versions - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf[offs]); - offs += buf_put_int16(ZIP_VERSION, &buf[offs]); + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); // General purpose flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf[offs]); + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); // Compression - offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf[offs]); + offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); // Modification date/time - offs += buf_put_int32(0, &buf[offs]); + offs += buf_put_int32(0, &buf.write[offs]); // Crc-32 - offs += buf_put_int32(p_file.file_crc32, &buf[offs]); + offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); // File sizes - offs += buf_put_int32(p_file.compressed_size, &buf[offs]); - offs += buf_put_int32(p_file.uncompressed_size, &buf[offs]); + offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); + offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); // File name length - offs += buf_put_int16(p_file.name.length(), &buf[offs]); + offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); // Extra field length - offs += buf_put_int16(0, &buf[offs]); + offs += buf_put_int16(0, &buf.write[offs]); // Comment length - offs += buf_put_int16(0, &buf[offs]); + offs += buf_put_int16(0, &buf.write[offs]); // Disk number start, internal/external file attributes for (int i = 0; i < 8; i++) { - buf[offs++] = 0; + buf.write[offs++] = 0; } // Relative offset - offs += buf_put_int32(p_file.zip_offset, &buf[offs]); + offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); // File name - offs += buf_put_string(p_file.name, &buf[offs]); + offs += buf_put_string(p_file.name, &buf.write[offs]); // Done! } @@ -397,62 +397,62 @@ Vector<uint8_t> AppxPackager::make_end_of_central_record() { int offs = 0; // Write magic - offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf[offs]); + offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); // Size of this record - offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf[offs]); + offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); // Version (yes, twice) - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf[offs]); - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf[offs]); + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); // Disk number for (int i = 0; i < 8; i++) { - buf[offs++] = 0; + buf.write[offs++] = 0; } // Number of entries (total and per disk) - offs += buf_put_int64(file_metadata.size(), &buf[offs]); - offs += buf_put_int64(file_metadata.size(), &buf[offs]); + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); // Size of central dir - offs += buf_put_int64(central_dir_data.size(), &buf[offs]); + offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); // Central dir offset - offs += buf_put_int64(central_dir_offset, &buf[offs]); + offs += buf_put_int64(central_dir_offset, &buf.write[offs]); ////// ZIP64 locator // Write magic for zip64 central dir locator - offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf[offs]); + offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); // Disk number for (int i = 0; i < 4; i++) { - buf[offs++] = 0; + buf.write[offs++] = 0; } // Relative offset - offs += buf_put_int64(end_of_central_dir_offset, &buf[offs]); + offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); // Number of disks - offs += buf_put_int32(1, &buf[offs]); + offs += buf_put_int32(1, &buf.write[offs]); /////// End of zip directory // Write magic for end central dir - offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf[offs]); + offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); // Dummy stuff for Zip64 for (int i = 0; i < 4; i++) { - buf[offs++] = 0x0; + buf.write[offs++] = 0x0; } for (int i = 0; i < 12; i++) { - buf[offs++] = 0xFF; + buf.write[offs++] = 0xFF; } // Size of comments for (int i = 0; i < 2; i++) { - buf[offs++] = 0; + buf.write[offs++] = 0; } // Done! @@ -508,7 +508,7 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t size_t block_size = (p_len - step) > BLOCK_SIZE ? BLOCK_SIZE : (p_len - step); for (uint32_t i = 0; i < block_size; i++) { - strm_in[i] = p_buffer[step + i]; + strm_in.write[i] = p_buffer[step + i]; } BlockHash bh; @@ -530,14 +530,14 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + bh.compressed_size); for (uint32_t i = 0; i < bh.compressed_size; i++) - file_buffer[start + i] = strm_out[i]; + file_buffer.write[start + i] = strm_out[i]; } else { bh.compressed_size = block_size; //package->store_buffer(strm_in.ptr(), block_size); int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + block_size); for (uint32_t i = 0; i < bh.compressed_size; i++) - file_buffer[start + i] = strm_in[i]; + file_buffer.write[start + i] = strm_in[i]; } meta.hashes.push_back(bh); @@ -560,7 +560,7 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); for (uint32_t i = 0; i < (strm.total_out - total_out_before); i++) - file_buffer[start + i] = strm_out[i]; + file_buffer.write[start + i] = strm_out[i]; deflateEnd(&strm); meta.compressed_size = strm.total_out; @@ -864,7 +864,7 @@ class EditorExportUWP : public EditorExportPlatform { r_ret.resize(result.length()); for (int i = 0; i < result.length(); i++) - r_ret[i] = result.utf8().get(i); + r_ret.write[i] = result.utf8().get(i); return r_ret; } @@ -1371,8 +1371,8 @@ public: CharString txt = cl[i].utf8(); int base = clf.size(); clf.resize(base + 4 + txt.length()); - encode_uint32(txt.length(), &clf[base]); - copymem(&clf[base + 4], txt.ptr(), txt.length()); + encode_uint32(txt.length(), &clf.write[base]); + copymem(&clf.write[base + 4], txt.ptr(), txt.length()); print_line(itos(i) + " param: " + cl[i]); } diff --git a/platform/windows/SCsub b/platform/windows/SCsub index ed3827353d..53ed3bf887 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -3,15 +3,8 @@ import os Import('env') -def make_debug_mingw(target, source, env): - mingw_prefix = "" - if (env["bits"] == "32"): - mingw_prefix = env["mingw_prefix_32"] - else: - mingw_prefix = env["mingw_prefix_64"] - os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) - os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0])) - os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) +from platform_methods import run_in_subprocess +import platform_windows_builders common_win = [ "context_gl_win.cpp", @@ -40,4 +33,4 @@ if env['vsproj']: if not os.getenv("VCINSTALLDIR"): if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: - env.AddPostAction(prog, make_debug_mingw) + env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index d312fbcb12..a158237418 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -106,9 +106,9 @@ Error ContextGL_Win::initialize() { PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, - 24, + OS::get_singleton()->is_layered_allowed() ? 32 : 24, 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 0, // No Alpha Buffer + OS::get_singleton()->is_layered_allowed() ? 8 : 0, // Alpha Buffer 0, // Shift Bit Ignored 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored diff --git a/platform/windows/detect.py b/platform/windows/detect.py index cacb573236..34fc3e09b5 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -206,7 +206,8 @@ def configure_msvc(env, manual_msvc_config): env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', - 'TYPED_METHOD_BIND', 'WIN32', 'MSVC', + 'WINMIDI_ENABLED', 'TYPED_METHOD_BIND', + 'WIN32', 'MSVC', {'WINVER' : '$target_win_version', '_WIN32_WINNT': '$target_win_version'}]) if env["bits"] == "64": diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h index 1a966b502a..78bfadfeae 100644 --- a/platform/windows/lang_table.h +++ b/platform/windows/lang_table.h @@ -186,6 +186,7 @@ static const _WinLocale _win_locales[] = { { "zh_CN", LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED }, { "zh_HK", LANG_CHINESE, SUBLANG_CHINESE_HONGKONG }, { "zh_SG", LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE }, + { "zh_TW", LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL }, { 0, 0, 0 }, }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ac37e1246d..033654323b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1219,6 +1219,10 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int AudioDriverManager::initialize(p_audio_driver); +#ifdef WINMIDI_ENABLED + driver_midi.open(); +#endif + TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; @@ -1347,6 +1351,10 @@ void OS_Windows::set_main_loop(MainLoop *p_main_loop) { void OS_Windows::finalize() { +#ifdef WINMIDI_ENABLED + driver_midi.close(); +#endif + if (main_loop) memdelete(main_loop); @@ -2291,7 +2299,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, Vector<CharType> modstr; //windows wants to change this no idea why modstr.resize(cmdline.size()); for (int i = 0; i < cmdline.size(); i++) - modstr[i] = cmdline[i]; + modstr.write[i] = cmdline[i]; int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, si_w, &pi.pi); ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); @@ -2301,6 +2309,8 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, if (r_exitcode) *r_exitcode = ret; + CloseHandle(pi.pi.hProcess); + CloseHandle(pi.pi.hThread); } else { ProcessID pid = pi.pi.dwProcessId; @@ -2314,17 +2324,15 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, Error OS_Windows::kill(const ProcessID &p_pid) { - HANDLE h; + ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); - if (process_map->has(p_pid)) { - h = (*process_map)[p_pid].pi.hProcess; - process_map->erase(p_pid); - } else { + const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi; + process_map->erase(p_pid); - ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); - }; + const int ret = TerminateProcess(pi.hProcess, 0); - int ret = TerminateProcess(h, 0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); return ret != 0 ? OK : FAILED; }; @@ -2362,7 +2370,7 @@ void OS_Windows::set_icon(const Ref<Image> &p_icon) { int icon_len = 40 + h * w * 4; Vector<BYTE> v; v.resize(icon_len); - BYTE *icon_bmp = &v[0]; + BYTE *icon_bmp = v.ptrw(); encode_uint32(40, &icon_bmp[0]); encode_uint32(w, &icon_bmp[4]); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index b99d3e3422..69c7d851b8 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -35,6 +35,7 @@ #include "crash_handler_win.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/wasapi/audio_driver_wasapi.h" +#include "drivers/winmidi/win_midi.h" #include "os/input.h" #include "os/os.h" #include "power_windows.h" @@ -144,6 +145,9 @@ class OS_Windows : public OS { #ifdef XAUDIO2_ENABLED AudioDriverXAudio2 driver_xaudio2; #endif +#ifdef WINMIDI_ENABLED + MIDIDriverWinMidi driver_midi; +#endif CrashHandler crash_handler; diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py new file mode 100644 index 0000000000..a1ad3b8b50 --- /dev/null +++ b/platform/windows/platform_windows_builders.py @@ -0,0 +1,22 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +from platform_methods import subprocess_main + + +def make_debug_mingw(target, source, env): + mingw_prefix = "" + if (env["bits"] == "32"): + mingw_prefix = env["mingw_prefix_32"] + else: + mingw_prefix = env["mingw_prefix_64"] + os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/platform/x11/SCsub b/platform/x11/SCsub index d0f77892ef..d3901eb798 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -3,10 +3,8 @@ import os Import('env') -def make_debug(target, source, env): - os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) - os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0])) - os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) +from platform_methods import run_in_subprocess +import platform_x11_builders common_x11 = [ "context_gl_x11.cpp", @@ -20,4 +18,4 @@ common_x11 = [ prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11) if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: - env.AddPostAction(prog, make_debug) + env.AddPostAction(prog, run_in_subprocess(platform_x11_builders.make_debug_x11)) diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 58610a0e55..6a7a426804 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -62,6 +62,7 @@ def get_opts(): EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')), BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False), BoolVariable('touch', 'Enable touch events', True), + BoolVariable('execinfo', 'Use libexecinfo on systems where glibc is not available', False), ] @@ -248,7 +249,7 @@ def configure(env): if (os.system("pkg-config --exists alsa") == 0): # 0 means found print("Enabling ALSA") - env.Append(CPPFLAGS=["-DALSA_ENABLED"]) + env.Append(CPPFLAGS=["-DALSA_ENABLED", "-DALSAMIDI_ENABLED"]) env.ParseConfig('pkg-config alsa --cflags --libs') else: print("ALSA libraries not found, disabling driver") @@ -284,6 +285,9 @@ def configure(env): env.Append(LIBS=['dl']) if (platform.system().find("BSD") >= 0): + env["execinfo"] = True + + if env["execinfo"]: env.Append(LIBS=['execinfo']) ## Cross-compilation diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index af0e02173f..9d1e3291b7 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -342,6 +342,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); +#ifdef ALSAMIDI_ENABLED + driver_alsamidi.open(); +#endif + ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE); @@ -606,6 +610,9 @@ void OS_X11::finalize() { memdelete(debugger_connection_console); } */ +#ifdef ALSAMIDI_ENABLED + driver_alsamidi.close(); +#endif #ifdef JOYDEV_ENABLED memdelete(joypad); @@ -1831,8 +1838,8 @@ void OS_X11::process_xevents() { GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } #ifdef TOUCH_ENABLED - // Grab touch devices to avoid OS gesture interference - /*for (int i = 0; i < touch.devices.size(); ++i) { + // Grab touch devices to avoid OS gesture interference + /*for (int i = 0; i < touch.devices.size(); ++i) { XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); }*/ #endif @@ -2098,7 +2105,7 @@ void OS_X11::process_xevents() { Vector<String> files = String((char *)p.data).split("\n", false); for (int i = 0; i < files.size(); i++) { - files[i] = files[i].replace("file://", "").replace("%20", " ").strip_escapes(); + files.write[i] = files[i].replace("file://", "").replace("%20", " ").strip_escapes(); } main_loop->drop_files(files); @@ -2580,12 +2587,12 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) { pd.resize(2 + w * h); - pd[0] = w; - pd[1] = h; + pd.write[0] = w; + pd.write[1] = h; PoolVector<uint8_t>::Read r = img->get_data().read(); - long *wr = &pd[2]; + long *wr = &pd.write[2]; uint8_t const *pr = r.ptr(); for (int i = 0; i < w * h; i++) { diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index b67bc824be..44455a2d8d 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -38,6 +38,7 @@ #include "servers/visual_server.h" //#include "servers/visual/visual_server_wrap_mt.h" #include "drivers/alsa/audio_driver_alsa.h" +#include "drivers/alsamidi/alsa_midi.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" #include "joypad_linux.h" #include "main/input_default.h" @@ -168,6 +169,10 @@ class OS_X11 : public OS_Unix { AudioDriverALSA driver_alsa; #endif +#ifdef ALSAMIDI_ENABLED + MIDIDriverALSAMidi driver_alsamidi; +#endif + #ifdef PULSEAUDIO_ENABLED AudioDriverPulseAudio driver_pulseaudio; #endif diff --git a/platform/x11/platform_x11_builders.py b/platform/x11/platform_x11_builders.py new file mode 100644 index 0000000000..5ff0c6fb14 --- /dev/null +++ b/platform/x11/platform_x11_builders.py @@ -0,0 +1,17 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +import os +from platform_methods import subprocess_main + + +def make_debug_x11(target, source, env): + os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/platform_methods.py b/platform_methods.py new file mode 100644 index 0000000000..4300216427 --- /dev/null +++ b/platform_methods.py @@ -0,0 +1,82 @@ +import os +import sys +import json +import uuid +import functools +import subprocess + +# NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle + +if sys.version_info[0] < 3: + JSON_SERIALIZABLE_TYPES = (bool, int, long, float, basestring) +else: + JSON_SERIALIZABLE_TYPES = (bool, int, float, str) + + +def run_in_subprocess(builder_function): + + @functools.wraps(builder_function) + def wrapper(target, source, env): + + # Convert SCons Node instances to absolute paths + target = [node.srcnode().abspath for node in target] + source = [node.srcnode().abspath for node in source] + + # Short circuit on non-Windows platforms, no need to run in subprocess + if sys.platform not in ('win32', 'cygwin'): + return builder_function(target, source, env) + + # Identify module + module_name = builder_function.__module__ + function_name = builder_function.__name__ + module_path = sys.modules[module_name].__file__ + if module_path.endswith('.pyc') or module_path.endswith('.pyo'): + module_path = module_path[:-1] + + # Subprocess environment + subprocess_env = os.environ.copy() + subprocess_env['PYTHONPATH'] = os.pathsep.join([os.getcwd()] + sys.path) + + # Keep only JSON serializable environment items + filtered_env = dict( + (key, value) + for key, value in env.items() + if isinstance(value, JSON_SERIALIZABLE_TYPES) + ) + + # Save parameters + args = (target, source, filtered_env) + data = dict(fn=function_name, args=args) + json_path = os.path.join(os.environ['TMP'], uuid.uuid4().hex + '.json') + with open(json_path, 'wt') as json_file: + json.dump(data, json_file, indent=2) + json_file_size = os.stat(json_path).st_size + + print('Executing builder function in subprocess: ' + 'module_path=%r, parameter_file=%r, parameter_file_size=%r, target=%r, source=%r' % ( + module_path, json_path, json_file_size, target, source)) + try: + exit_code = subprocess.call([sys.executable, module_path, json_path], env=subprocess_env) + finally: + try: + os.remove(json_path) + except (OSError, IOError) as e: + # Do not fail the entire build if it cannot delete a temporary file + print('WARNING: Could not delete temporary file: path=%r; [%s] %s' % + (json_path, e.__class__.__name__, e)) + + # Must succeed + if exit_code: + raise RuntimeError( + 'Failed to run builder function in subprocess: module_path=%r; data=%r' % (module_path, data)) + + return wrapper + + +def subprocess_main(namespace): + + with open(sys.argv[1]) as json_file: + data = json.load(json_file) + + fn = namespace[data['fn']] + fn(*data['args']) diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index b56eedabc7..85e7f8df92 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -235,7 +235,7 @@ void SpriteFrames::_set_frames(const Array &p_frames) { E->get().frames.resize(p_frames.size()); for (int i = 0; i < E->get().frames.size(); i++) - E->get().frames[i] = p_frames[i]; + E->get().frames.write[i] = p_frames[i]; } Array SpriteFrames::_get_frames() const { diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index f6586aff36..cc49465403 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -113,7 +113,7 @@ public: ERR_FAIL_COND(p_idx < 0); if (p_idx >= E->get().frames.size()) return; - E->get().frames[p_idx] = p_frame; + E->get().frames.write[p_idx] = p_frame; } void remove_frame(const StringName &p_anim, int p_idx); void clear(const StringName &p_anim); diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 47326b9be2..a035d9021f 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -411,6 +411,9 @@ void CanvasItem::_enter_canvas() { if (canvas_layer) { break; } + if (Object::cast_to<Viewport>(n)) { + break; + } n = n->get_parent(); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index cabd7fddc2..1e2184bd41 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -259,7 +259,7 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { for (int i = 0; i < E->get().shapes.size(); i++) { if (E->get().shapes[i].index > index_to_remove) { - E->get().shapes[i].index -= 1; + E->get().shapes.write[i].index -= 1; } } } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 9d2a83fda7..9f19f56e75 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -102,11 +102,11 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() { TriangulatorPoly &tp = I->get(); - decomp[idx].resize(tp.GetNumPoints()); + decomp.write[idx].resize(tp.GetNumPoints()); for (int i = 0; i < tp.GetNumPoints(); i++) { - decomp[idx][i] = tp.GetPoint(i); + decomp.write[idx].write[i] = tp.GetPoint(i); } idx++; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index e9e895b5bb..ad9775c0b7 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -239,7 +239,7 @@ void Line2D::_draw() { { PoolVector<Vector2>::Read points_read = _points.read(); for (int i = 0; i < len; ++i) { - points[i] = points_read[i]; + points.write[i] = points_read[i]; } } diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 74ad3e79d0..a3f1b25e05 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -292,7 +292,6 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx0 = current_distance0 / (width * tile_aspect); uvx1 = current_distance1 / (width * tile_aspect); } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { uvx0 = current_distance0 / total_distance; @@ -306,7 +305,6 @@ void LineBuilder::build() { u0 = u1; f0 = f1; pos0 = pos1; - current_distance0 = current_distance1; if (intersection_result == SEGMENT_INTERSECT) { if (current_joint_mode == Line2D::LINE_JOINT_SHARP) { pos_up0 = pos_up1; diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp index 16e0386952..e3b048fd74 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -74,7 +74,7 @@ void Navigation2D::_navpoly_link(int p_id) { Vector2 ep = nm.xform.xform(r[idx]); center += ep; e.point = _get_point(ep); - p.edges[j] = e; + p.edges.write[j] = e; int idxn = indices[(j + 1) % plen]; if (idxn < 0 || idxn >= len) { @@ -119,17 +119,17 @@ void Navigation2D::_navpoly_link(int p_id) { ConnectionPending pending; pending.polygon = &p; pending.edge = j; - p.edges[j].P = C->get().pending.push_back(pending); + p.edges.write[j].P = C->get().pending.push_back(pending); continue; //print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b)); } C->get().B = &p; C->get().B_edge = j; - C->get().A->edges[C->get().A_edge].C = &p; - C->get().A->edges[C->get().A_edge].C_edge = j; - p.edges[j].C = C->get().A; - p.edges[j].C_edge = C->get().A_edge; + C->get().A->edges.write[C->get().A_edge].C = &p; + C->get().A->edges.write[C->get().A_edge].C_edge = j; + p.edges.write[j].C = C->get().A; + p.edges.write[j].C_edge = C->get().A_edge; //connection successful. } } @@ -167,10 +167,10 @@ void Navigation2D::_navpoly_unlink(int p_id) { } else if (C->get().B) { //disconnect - C->get().B->edges[C->get().B_edge].C = NULL; - C->get().B->edges[C->get().B_edge].C_edge = -1; - C->get().A->edges[C->get().A_edge].C = NULL; - C->get().A->edges[C->get().A_edge].C_edge = -1; + C->get().B->edges.write[C->get().B_edge].C = NULL; + C->get().B->edges.write[C->get().B_edge].C_edge = -1; + C->get().A->edges.write[C->get().A_edge].C = NULL; + C->get().A->edges.write[C->get().A_edge].C_edge = -1; if (C->get().A == &E->get()) { @@ -187,11 +187,11 @@ void Navigation2D::_navpoly_unlink(int p_id) { C->get().B = cp.polygon; C->get().B_edge = cp.edge; - C->get().A->edges[C->get().A_edge].C = cp.polygon; - C->get().A->edges[C->get().A_edge].C_edge = cp.edge; - cp.polygon->edges[cp.edge].C = C->get().A; - cp.polygon->edges[cp.edge].C_edge = C->get().A_edge; - cp.polygon->edges[cp.edge].P = NULL; + C->get().A->edges.write[C->get().A_edge].C = cp.polygon; + C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; + cp.polygon->edges.write[cp.edge].C = C->get().A; + cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; + cp.polygon->edges.write[cp.edge].P = NULL; } } else { @@ -339,8 +339,8 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect Vector<Vector2> path; path.resize(2); - path[0] = begin_point; - path[1] = end_point; + path.write[0] = begin_point; + path.write[1] = end_point; //print_line("Direct Path"); return path; } @@ -408,7 +408,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect for (int i = 0; i < es; i++) { - Polygon::Edge &e = p->edges[i]; + Polygon::Edge &e = p->edges.write[i]; if (!e.C) continue; @@ -586,7 +586,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) { path.push_back(begin_point); // Add the begin point } else { - path[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point + path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point } path.invert(); @@ -594,7 +594,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) { path.push_back(end_point); // Add the end point } else { - path[path.size() - 1] = end_point; // Replace last midpoint by the exact end point + path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point } return path; diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 6e27bf1c1d..2d6679272a 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -91,7 +91,7 @@ void NavigationPolygon::_set_polygons(const Array &p_array) { polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons[i].indices = p_array[i]; + polygons.write[i].indices = p_array[i]; } } @@ -110,7 +110,7 @@ void NavigationPolygon::_set_outlines(const Array &p_array) { outlines.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - outlines[i] = p_array[i]; + outlines.write[i] = p_array[i]; } rect_cache_dirty = true; } @@ -166,7 +166,7 @@ int NavigationPolygon::get_outline_count() const { void NavigationPolygon::set_outline(int p_idx, const PoolVector<Vector2> &p_outline) { ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines[p_idx] = p_outline; + outlines.write[p_idx] = p_outline; rect_cache_dirty = true; } @@ -432,8 +432,8 @@ void NavigationPolygonInstance::_notification(int p_what) { { PoolVector<Vector2>::Read vr = verts.read(); for (int i = 0; i < vsize; i++) { - vertices[i] = vr[i]; - colors[i] = color; + vertices.write[i] = vr[i]; + colors.write[i] = color; } } diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8787a2c735..8758ffef9f 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -30,6 +30,7 @@ #include "physics_body_2d.h" +#include "core/core_string_names.h" #include "core/method_bind_ext.gen.inc" #include "engine.h" #include "math_funcs.h" @@ -186,28 +187,76 @@ real_t StaticBody2D::get_constant_angular_velocity() const { return constant_angular_velocity; } +#ifndef DISABLE_DEPRECATED void StaticBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } + real_t StaticBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } + real_t StaticBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const { + return physics_material_override; } void StaticBody2D::_bind_methods() { @@ -216,29 +265,48 @@ void StaticBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity); ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity); ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity); + +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &StaticBody2D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &StaticBody2D::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody2D::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody2D::_reload_physics_characteristics); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); } StaticBody2D::StaticBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) { constant_angular_velocity = 0; - bounce = 0; - friction = 1; } StaticBody2D::~StaticBody2D() { } +void StaticBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + void RigidBody2D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); @@ -545,28 +613,73 @@ real_t RigidBody2D::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10; } +#ifndef DISABLE_DEPRECATED void RigidBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } real_t RigidBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } real_t RigidBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { + return physics_material_override; } void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { @@ -690,11 +803,19 @@ int RigidBody2D::get_max_contacts_reported() const { return max_contacts_reported; } +void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { + Physics2DServer::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); +} + void RigidBody2D::apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse) { Physics2DServer::get_singleton()->body_apply_impulse(get_rid(), p_offset, p_impulse); } +void RigidBody2D::apply_torque_impulse(float p_torque) { + Physics2DServer::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); +} + void RigidBody2D::set_applied_force(const Vector2 &p_force) { Physics2DServer::get_singleton()->body_set_applied_force(get_rid(), p_force); @@ -715,11 +836,19 @@ float RigidBody2D::get_applied_torque() const { return Physics2DServer::get_singleton()->body_get_applied_torque(get_rid()); }; +void RigidBody2D::add_central_force(const Vector2 &p_force) { + Physics2DServer::get_singleton()->body_add_central_force(get_rid(), p_force); +} + void RigidBody2D::add_force(const Vector2 &p_offset, const Vector2 &p_force) { Physics2DServer::get_singleton()->body_add_force(get_rid(), p_offset, p_force); } +void RigidBody2D::add_torque(const float p_torque) { + Physics2DServer::get_singleton()->body_add_torque(get_rid(), p_torque); +} + void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { ccd_mode = p_mode; @@ -837,11 +966,18 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody2D::set_weight); ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody2D::get_weight); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &RigidBody2D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &RigidBody2D::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody2D::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody2D::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale); @@ -871,7 +1007,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidBody2D::get_continuous_collision_detection_mode); ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody2D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody2D::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "offset", "impulse"), &RigidBody2D::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidBody2D::apply_torque_impulse); ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidBody2D::set_applied_force); ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidBody2D::get_applied_force); @@ -879,7 +1017,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidBody2D::set_applied_torque); ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidBody2D::get_applied_torque); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody2D::add_central_force); ClassDB::bind_method(D_METHOD("add_force", "offset", "force"), &RigidBody2D::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody2D::add_torque); ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody2D::set_sleeping); ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody2D::is_sleeping); @@ -901,8 +1041,11 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode"); @@ -941,9 +1084,7 @@ RigidBody2D::RigidBody2D() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; gravity_scale = 1; linear_damp = -1; @@ -969,6 +1110,16 @@ RigidBody2D::~RigidBody2D() { memdelete(contact_monitor); } +void RigidBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + ////////////////////////// Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) { @@ -1053,6 +1204,9 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ return colliding; } +//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +#define FLOOR_ANGLE_THRESHOLD 0.01 + Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { Vector2 floor_motion = floor_velocity; @@ -1106,7 +1260,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const //all is a wall on_wall = true; } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor on_floor = true; on_floor_body = collision.collider_rid; @@ -1121,7 +1275,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const set_global_transform(gt); return Vector2(); } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling on_ceiling = true; } else { on_wall = true; @@ -1225,11 +1379,11 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders[p_bounce].instance(); - slide_colliders[p_bounce]->owner = this; + slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce]->owner = this; } - slide_colliders[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; return slide_colliders[p_bounce]; } @@ -1327,7 +1481,7 @@ KinematicBody2D::~KinematicBody2D() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { - slide_colliders[i]->owner = NULL; + slide_colliders.write[i]->owner = NULL; } } } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 7bda6ce817..0a2ce0918b 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -32,6 +32,7 @@ #define PHYSICS_BODY_2D_H #include "scene/2d/collision_object_2d.h" +#include "scene/resources/physics_material.h" #include "servers/physics_2d_server.h" #include "vset.h" @@ -79,18 +80,21 @@ class StaticBody2D : public PhysicsBody2D { Vector2 constant_linear_velocity; real_t constant_angular_velocity; - real_t bounce; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; protected: static void _bind_methods(); public: +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_constant_linear_velocity(const Vector2 &p_vel); void set_constant_angular_velocity(real_t p_vel); @@ -100,6 +104,9 @@ public: StaticBody2D(); ~StaticBody2D(); + +private: + void _reload_physics_characteristics(); }; class RigidBody2D : public PhysicsBody2D { @@ -125,9 +132,8 @@ private: Physics2DDirectBodyState *state; Mode mode; - real_t bounce; real_t mass; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; real_t gravity_scale; real_t linear_damp; real_t angular_damp; @@ -204,11 +210,16 @@ public: void set_weight(real_t p_weight); real_t get_weight() const; +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; @@ -245,7 +256,9 @@ public: void set_continuous_collision_detection_mode(CCDMode p_mode); CCDMode get_continuous_collision_detection_mode() const; + void apply_central_impulse(const Vector2 &p_impulse); void apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse); + void apply_torque_impulse(float p_torque); void set_applied_force(const Vector2 &p_force); Vector2 get_applied_force() const; @@ -253,7 +266,9 @@ public: void set_applied_torque(const float p_torque); float get_applied_torque() const; + void add_central_force(const Vector2 &p_force); void add_force(const Vector2 &p_offset, const Vector2 &p_force); + void add_torque(float p_torque); Array get_colliding_bodies() const; //function for script @@ -261,6 +276,9 @@ public: RigidBody2D(); ~RigidBody2D(); + +private: + void _reload_physics_characteristics(); }; VARIANT_ENUM_CAST(RigidBody2D::Mode); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 81ed3c63c3..c9e5408f06 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -113,7 +113,7 @@ void Polygon2D::_notification(int p_what) { PoolVector<Vector2>::Read polyr = polygon.read(); for (int i = 0; i < len; i++) { - points[i] = polyr[i] + offset; + points.write[i] = polyr[i] + offset; } } @@ -153,18 +153,18 @@ void Polygon2D::_notification(int p_what) { SWAP(ep[1], ep[4]); SWAP(ep[2], ep[3]); SWAP(ep[5], ep[0]); - SWAP(ep[6], points[highest_idx]); + SWAP(ep[6], points.write[highest_idx]); } points.resize(points.size() + 7); for (int i = points.size() - 1; i >= highest_idx + 7; i--) { - points[i] = points[i - 7]; + points.write[i] = points[i - 7]; } for (int i = 0; i < 7; i++) { - points[highest_idx + i + 1] = ep[i]; + points.write[highest_idx + i + 1] = ep[i]; } len = points.size(); @@ -182,12 +182,12 @@ void Polygon2D::_notification(int p_what) { PoolVector<Vector2>::Read uvr = uv.read(); for (int i = 0; i < len; i++) { - uvs[i] = texmat.xform(uvr[i]) / tex_size; + uvs.write[i] = texmat.xform(uvr[i]) / tex_size; } } else { for (int i = 0; i < len; i++) { - uvs[i] = texmat.xform(points[i]) / tex_size; + uvs.write[i] = texmat.xform(points[i]) / tex_size; } } } @@ -262,10 +262,10 @@ void Polygon2D::_notification(int p_what) { { PoolVector<Color>::Read color_r = vertex_colors.read(); for (int i = 0; i < color_len && i < len; i++) { - colors[i] = color_r[i]; + colors.write[i] = color_r[i]; } for (int i = color_len; i < len; i++) { - colors[i] = color; + colors.write[i] = color; } } @@ -333,13 +333,13 @@ void Polygon2D::_notification(int p_what) { Vector<Vector2> vertices; vertices.resize(loop.size()); for (int j = 0; j < vertices.size(); j++) { - vertices[j] = points[loop[j]]; + vertices.write[j] = points[loop[j]]; } Vector<int> sub_indices = Geometry::triangulate_polygon(vertices); int from = indices.size(); indices.resize(from + sub_indices.size()); for (int j = 0; j < sub_indices.size(); j++) { - indices[from + j] = loop[sub_indices[j]]; + indices.write[from + j] = loop[sub_indices[j]]; } } @@ -538,12 +538,12 @@ void Polygon2D::clear_bones() { void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) { ERR_FAIL_INDEX(p_index, bone_weights.size()); - bone_weights[p_index].weights = p_weights; + bone_weights.write[p_index].weights = p_weights; update(); } void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { ERR_FAIL_INDEX(p_index, bone_weights.size()); - bone_weights[p_index].path = p_path; + bone_weights.write[p_index].path = p_path; update(); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8ceffb3c27..e6e9bde20a 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -191,13 +191,13 @@ void Skeleton2D::_update_bone_setup() { bones.sort(); //sorty so they are always in the same order/index for (int i = 0; i < bones.size(); i++) { - bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose - bones[i].bone->skeleton_index = i; + bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose + bones.write[i].bone->skeleton_index = i; Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent()); if (parent_bone) { - bones[i].parent_index = parent_bone->skeleton_index; + bones.write[i].parent_index = parent_bone->skeleton_index; } else { - bones[i].parent_index = -1; + bones.write[i].parent_index = -1; } } @@ -230,9 +230,9 @@ void Skeleton2D::_update_transform() { ERR_CONTINUE(bones[i].parent_index >= i); if (bones[i].parent_index >= 0) { - bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); + bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); } else { - bones[i].accum_transform = bones[i].bone->get_transform(); + bones.write[i].accum_transform = bones[i].bone->get_transform(); } } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 72c3ed7425..80565fa455 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -368,7 +368,7 @@ void TileMap::update_dirty_quadrants() { } Rect2 r = tile_set->tile_get_region(c.id); - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { int spacing = tile_set->autotile_get_spacing(c.id); r.size = tile_set->autotile_get_size(c.id); r.position += (r.size + Vector2(spacing, spacing)) * Vector2(c.autotile_coord_x, c.autotile_coord_y); @@ -491,7 +491,7 @@ void TileMap::update_dirty_quadrants() { if (navigation) { Ref<NavigationPolygon> navpoly; Vector2 npoly_ofs; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { navpoly = tile_set->autotile_get_navigation_polygon(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); npoly_ofs = Vector2(); } else { @@ -529,8 +529,8 @@ void TileMap::update_dirty_quadrants() { { PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read(); for (int i = 0; i < vsize; i++) { - vertices[i] = vr[i]; - colors[i] = debug_navigation_color; + vertices.write[i] = vr[i]; + colors.write[i] = debug_navigation_color; } } @@ -563,7 +563,7 @@ void TileMap::update_dirty_quadrants() { } Ref<OccluderPolygon2D> occluder; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); } else { occluder = tile_set->tile_get_light_occluder(c.id); @@ -631,11 +631,7 @@ void TileMap::_recompute_rect_cache() { r_total = r_total.merge(r); } - if (r_total == Rect2()) { - rect_cache = Rect2(-10, -10, 20, 20); - } else { - rect_cache = r_total.grow(MAX(cell_size.x, cell_size.y) * _get_quadrant_size()); - } + rect_cache = r_total; item_rect_changed(); @@ -840,7 +836,7 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { Map<PosKey, Cell>::Element *E = tile_map.find(p); if (E != NULL) { int id = get_cell(p_x, p_y); - if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) { uint16_t mask = 0; if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_2X2) { if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { @@ -1152,6 +1148,11 @@ PoolVector<int> TileMap::_get_tile_data() const { return data; } +Rect2 TileMap::_edit_get_rect() const { + const_cast<TileMap *>(this)->update_dirty_quadrants(); + return rect_cache; +} + void TileMap::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; @@ -1611,7 +1612,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_celld", "data"), &TileMap::set_celld); + ClassDB::bind_method(D_METHOD("set_celld", "position", "data"), &TileMap::set_celld); ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell); ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv); ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index c8aceac17d..52aa6e8e2a 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -223,6 +223,8 @@ public: INVALID_CELL = -1 }; + virtual Rect2 _edit_get_rect() const; + void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 9a2046991b..2176b45faf 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -74,10 +74,7 @@ void Camera::_update_camera() { if (!is_inside_tree()) return; - Transform tr = get_camera_transform(); - tr.origin += tr.basis.get_axis(1) * v_offset; - tr.origin += tr.basis.get_axis(0) * h_offset; - VisualServer::get_singleton()->camera_set_transform(camera, tr); + VisualServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); // here goes listener stuff /* @@ -143,7 +140,10 @@ void Camera::_notification(int p_what) { Transform Camera::get_camera_transform() const { - return get_global_transform().orthonormalized(); + Transform tr = get_global_transform().orthonormalized(); + tr.origin += tr.basis.get_axis(1) * v_offset; + tr.origin += tr.basis.get_axis(0) * h_offset; + return tr; } void Camera::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { @@ -468,6 +468,10 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); + + ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit); + ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit); + //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); @@ -550,6 +554,20 @@ uint32_t Camera::get_cull_mask() const { return layers; } +void Camera::set_cull_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_cull_mask(layers | (1 << p_layer)); + } else { + set_cull_mask(layers & (~(1 << p_layer))); + } +} + +bool Camera::get_cull_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + Vector<Plane> Camera::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 1b506e0c4f..97705d8ae0 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -142,6 +142,9 @@ public: void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; + void set_cull_mask_bit(int p_layer, bool p_enable); + bool get_cull_mask_bit(int p_layer) const; + virtual Vector<Plane> get_frustum() const; void set_environment(const Ref<Environment> &p_environment); diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 1d5d1b2afe..e19e45b263 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -305,7 +305,7 @@ void CollisionObject::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { for (int i = 0; i < E->get().shapes.size(); i++) { if (E->get().shapes[i].index > index_to_remove) { - E->get().shapes[i].index -= 1; + E->get().shapes.write[i].index -= 1; } } } diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index f31d65e411..f8ef04b78f 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -39,6 +39,7 @@ class CollisionObject : public Spatial { GDCLASS(CollisionObject, Spatial); bool area; + RID rid; struct ShapeData { diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index c9c91a5824..6ca8e80ea1 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -49,6 +49,7 @@ class CollisionShape : public Spatial { void resource_changed(RES res); bool disabled; +protected: void _create_debug_shape(); void _update_in_shape_owner(bool p_xform_only = false); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 2e897c1c73..8b2000d2e9 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -25,6 +25,8 @@ void CPUParticles::set_emitting(bool p_emitting) { update_mutex->lock(); #endif VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); + #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -446,6 +448,8 @@ float rand_from_seed_m1_p1(uint32_t &seed) { void CPUParticles::_particles_process(float p_delta) { + p_delta *= speed_scale; + int pcount = particles.size(); PoolVector<Particle>::Write w = particles.write(); @@ -475,7 +479,7 @@ void CPUParticles::_particles_process(float p_delta) { if (!emitting && !p.active) continue; - float restart_time = float(i) / float(pcount); + float restart_time = (float(i) / float(pcount)) * lifetime; float local_delta = p_delta; if (randomness_ratio > 0.0) { @@ -643,7 +647,7 @@ void CPUParticles::_particles_process(float p_delta) { uint32_t alt_seed = p.seed; p.time += local_delta; - p.custom[1] += p.time / lifetime; + p.custom[1] = p.time / lifetime; float tex_linear_velocity = 0.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { @@ -979,6 +983,7 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -992,6 +997,7 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -1018,6 +1024,8 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); + #ifndef NO_THREADS update_mutex->unlock(); #endif diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index e836a6154a..e277cae5b7 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -36,6 +36,7 @@ #include "scene/resources/material.h" #include "scene/scene_string_names.h" #include "skeleton.h" + bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -256,7 +257,7 @@ void MeshInstance::set_surface_material(int p_surface, const Ref<Material> &p_ma ERR_FAIL_INDEX(p_surface, materials.size()); - materials[p_surface] = p_material; + materials.write[p_surface] = p_material; if (materials[p_surface].is_valid()) VS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, materials[p_surface]->get_rid()); diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 5d359cd4d5..0dfec538f9 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance { GDCLASS(MeshInstance, GeometryInstance); +protected: Ref<Mesh> mesh; NodePath skeleton_path; diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 77bf703706..f5b77d361c 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -73,7 +73,7 @@ void Navigation::_navmesh_link(int p_id) { Vector3 ep = nm.xform.xform(r[idx]); center += ep; e.point = _get_point(ep); - p.edges[j] = e; + p.edges.write[j] = e; if (j >= 2) { Vector3 epa = nm.xform.xform(r[indices[j - 2]]); @@ -118,7 +118,7 @@ void Navigation::_navmesh_link(int p_id) { ConnectionPending pending; pending.polygon = &p; pending.edge = j; - p.edges[j].P = C->get().pending.push_back(pending); + p.edges.write[j].P = C->get().pending.push_back(pending); continue; //print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b)); } @@ -126,10 +126,10 @@ void Navigation::_navmesh_link(int p_id) { C->get().B = &p; C->get().B_edge = j; - C->get().A->edges[C->get().A_edge].C = &p; - C->get().A->edges[C->get().A_edge].C_edge = j; - p.edges[j].C = C->get().A; - p.edges[j].C_edge = C->get().A_edge; + C->get().A->edges.write[C->get().A_edge].C = &p; + C->get().A->edges.write[C->get().A_edge].C_edge = j; + p.edges.write[j].C = C->get().A; + p.edges.write[j].C_edge = C->get().A_edge; //connection successful. } } @@ -165,10 +165,10 @@ void Navigation::_navmesh_unlink(int p_id) { } else if (C->get().B) { //disconnect - C->get().B->edges[C->get().B_edge].C = NULL; - C->get().B->edges[C->get().B_edge].C_edge = -1; - C->get().A->edges[C->get().A_edge].C = NULL; - C->get().A->edges[C->get().A_edge].C_edge = -1; + C->get().B->edges.write[C->get().B_edge].C = NULL; + C->get().B->edges.write[C->get().B_edge].C_edge = -1; + C->get().A->edges.write[C->get().A_edge].C = NULL; + C->get().A->edges.write[C->get().A_edge].C_edge = -1; if (C->get().A == &E->get()) { @@ -185,11 +185,11 @@ void Navigation::_navmesh_unlink(int p_id) { C->get().B = cp.polygon; C->get().B_edge = cp.edge; - C->get().A->edges[C->get().A_edge].C = cp.polygon; - C->get().A->edges[C->get().A_edge].C_edge = cp.edge; - cp.polygon->edges[cp.edge].C = C->get().A; - cp.polygon->edges[cp.edge].C_edge = C->get().A_edge; - cp.polygon->edges[cp.edge].P = NULL; + C->get().A->edges.write[C->get().A_edge].C = cp.polygon; + C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; + cp.polygon->edges.write[cp.edge].C = C->get().A; + cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; + cp.polygon->edges.write[cp.edge].P = NULL; } } else { @@ -320,8 +320,8 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector Vector<Vector3> path; path.resize(2); - path[0] = begin_point; - path[1] = end_point; + path.write[0] = begin_point; + path.write[1] = end_point; //print_line("Direct Path"); return path; } @@ -375,7 +375,7 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector for (int i = 0; i < p->edges.size(); i++) { - Polygon::Edge &e = p->edges[i]; + Polygon::Edge &e = p->edges.write[i]; if (!e.C) continue; diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 073e56fdb4..99680b7273 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -55,9 +55,9 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { for (int j = 0; j < rlen; j += 3) { Vector<int> vi; vi.resize(3); - vi[0] = r[j + 0] + from; - vi[1] = r[j + 1] + from; - vi[2] = r[j + 2] + from; + vi.write[0] = r[j + 0] + from; + vi.write[1] = r[j + 1] + from; + vi.write[2] = r[j + 2] + from; add_polygon(vi); } @@ -215,7 +215,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons[i].indices = p_array[i]; + polygons.write[i].indices = p_array[i]; } } diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 2b3a62fcdc..4900692155 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -185,7 +185,7 @@ void Particles::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); - draw_passes[p_pass] = p_mesh; + draw_passes.write[p_pass] = p_mesh; RID mesh_rid; if (p_mesh.is_valid()) diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index e851c8d643..84a0fb9b1d 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -30,6 +30,7 @@ #include "physics_body.h" +#include "core/core_string_names.h" #include "engine.h" #include "method_bind_ext.gen.inc" #include "scene/scene_string_names.h" @@ -121,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const { void PhysicsBody::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::_set_layers(uint32_t p_mask) { @@ -178,28 +179,77 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : collision_mask = 1; } +#ifndef DISABLE_DEPRECATED void StaticBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + + physics_material_override->set_friction(p_friction); } + real_t StaticBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } + real_t StaticBody::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif + +void StaticBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody::get_physics_material_override() const { + return physics_material_override; } void StaticBody::set_constant_linear_velocity(const Vector3 &p_vel) { @@ -230,30 +280,45 @@ void StaticBody::_bind_methods() { ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody::get_constant_linear_velocity); ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody::get_constant_angular_velocity); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &StaticBody::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &StaticBody::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); - +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); } StaticBody::StaticBody() : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) { - - bounce = 0; - friction = 1; } -StaticBody::~StaticBody() { +StaticBody::~StaticBody() {} + +void StaticBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } } void RigidBody::_body_enter_tree(ObjectID p_id) { @@ -550,28 +615,68 @@ real_t RigidBody::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); } +#ifndef DISABLE_DEPRECATED void RigidBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } real_t RigidBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody::set_bounce(real_t p_bounce) { - + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } real_t RigidBody::get_bounce() const { + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 0; + } - return bounce; + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void RigidBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody::get_physics_material_override() const { + return physics_material_override; } void RigidBody::set_gravity_scale(real_t p_gravity_scale) { @@ -693,6 +798,22 @@ int RigidBody::get_max_contacts_reported() const { return max_contacts_reported; } +void RigidBody::add_central_force(const Vector3 &p_force) { + PhysicsServer::get_singleton()->body_add_central_force(get_rid(), p_force); +} + +void RigidBody::add_force(const Vector3 &p_force, const Vector3 &p_pos) { + PhysicsServer::get_singleton()->body_add_force(get_rid(), p_force, p_pos); +} + +void RigidBody::add_torque(const Vector3 &p_torque) { + PhysicsServer::get_singleton()->body_add_torque(get_rid(), p_torque); +} + +void RigidBody::apply_central_impulse(const Vector3 &p_impulse) { + PhysicsServer::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); +} + void RigidBody::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) { PhysicsServer::get_singleton()->body_apply_impulse(get_rid(), p_pos, p_impulse); @@ -806,11 +927,18 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody::set_weight); ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody::get_weight); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &RigidBody::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &RigidBody::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody::set_linear_velocity); ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody::get_linear_velocity); @@ -840,6 +968,12 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidBody::is_using_continuous_collision_detection); ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody::set_axis_velocity); + + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody::add_central_force); + ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidBody::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody::add_torque); + + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "impulse"), &RigidBody::apply_impulse); ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody::apply_torque_impulse); @@ -863,8 +997,11 @@ void RigidBody::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection"); @@ -903,9 +1040,7 @@ RigidBody::RigidBody() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; max_contacts_reported = 0; state = NULL; @@ -929,6 +1064,17 @@ RigidBody::~RigidBody() { if (contact_monitor) memdelete(contact_monitor); } + +void RigidBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + ////////////////////////////////////////////////////// ////////////////////////// @@ -979,6 +1125,9 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in return colliding; } +//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +#define FLOOR_ANGLE_THRESHOLD 0.01 + Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { Vector3 lv = p_linear_velocity; @@ -1011,7 +1160,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve //all is a wall on_wall = true; } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor on_floor = true; floor_velocity = collision.collider_vel; @@ -1025,7 +1174,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve set_global_transform(gt); return floor_velocity - p_floor_direction * p_floor_direction.dot(floor_velocity); } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling on_ceiling = true; } else { on_wall = true; @@ -1117,11 +1266,11 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders[p_bounce].instance(); - slide_colliders[p_bounce]->owner = this; + slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce]->owner = this; } - slide_colliders[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; return slide_colliders[p_bounce]; } @@ -1174,7 +1323,7 @@ KinematicBody::~KinematicBody() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { - slide_colliders[i]->owner = NULL; + slide_colliders.write[i]->owner = NULL; } } } @@ -2228,6 +2377,7 @@ void PhysicalBone::set_bounce(real_t p_bounce) { bounce = p_bounce; PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); } + real_t PhysicalBone::get_bounce() const { return bounce; diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 7236eba685..80bf422c98 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -32,6 +32,7 @@ #define PHYSICS_BODY__H #include "scene/3d/collision_object.h" +#include "scene/resources/physics_material.h" #include "servers/physics_server.h" #include "skeleton.h" #include "vset.h" @@ -81,18 +82,22 @@ class StaticBody : public PhysicsBody { Vector3 constant_linear_velocity; Vector3 constant_angular_velocity; - real_t bounce; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; protected: static void _bind_methods(); public: +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_constant_linear_velocity(const Vector3 &p_vel); void set_constant_angular_velocity(const Vector3 &p_vel); @@ -102,6 +107,9 @@ public: StaticBody(); ~StaticBody(); + +private: + void _reload_physics_characteristics(); }; class RigidBody : public PhysicsBody { @@ -121,9 +129,8 @@ protected: PhysicsDirectBodyState *state; Mode mode; - real_t bounce; real_t mass; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; Vector3 linear_velocity; Vector3 angular_velocity; @@ -196,11 +203,16 @@ public: void set_weight(real_t p_weight); real_t get_weight() const; +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_linear_velocity(const Vector3 &p_velocity); Vector3 get_linear_velocity() const; @@ -242,6 +254,11 @@ public: Array get_colliding_bodies() const; + void add_central_force(const Vector3 &p_force); + void add_force(const Vector3 &p_force, const Vector3 &p_pos); + void add_torque(const Vector3 &p_torque); + + void apply_central_impulse(const Vector3 &p_impulse); void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); void apply_torque_impulse(const Vector3 &p_impulse); @@ -249,6 +266,9 @@ public: RigidBody(); ~RigidBody(); + +private: + void _reload_physics_characteristics(); }; VARIANT_ENUM_CAST(RigidBody::Mode); @@ -537,6 +557,7 @@ protected: private: static Skeleton *find_skeleton_parent(Node *p_parent); + void _fix_joint_offset(); void _reload_joint(); diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 8d91b6f09f..c796e47f25 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -66,7 +66,7 @@ bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { Array children = p_value; if (is_inside_tree()) { - bones[which].nodes_bound.clear(); + bones.write[which].nodes_bound.clear(); for (int i = 0; i < children.size(); i++) { @@ -131,7 +131,7 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const { String prep = "bones/" + itos(i) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, prep + "name")); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(i - 1) + ",1")); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1")); p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest")); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled")); p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); @@ -139,6 +139,59 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const { } } +void Skeleton::_update_process_order() { + + if (!process_order_dirty) + return; + + Bone *bonesptr = bones.ptrw(); + int len = bones.size(); + + process_order.resize(len); + int *order = process_order.ptrw(); + for (int i = 0; i < len; i++) { + + if (bonesptr[i].parent >= len) { + //validate this just in case + ERR_PRINTS("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); + bonesptr[i].parent = -1; + } + order[i] = i; + bonesptr[i].sort_index = i; + } + //now check process order + int pass_count = 0; + while (pass_count < len * len) { + //using bubblesort because of simplicity, it wont run every frame though. + //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic + bool swapped = false; + for (int i = 0; i < len; i++) { + int parent_idx = bonesptr[order[i]].parent; + if (parent_idx < 0) + continue; //do nothing because it has no parent + //swap indices + int parent_order = bonesptr[parent_idx].sort_index; + if (parent_order > i) { + bonesptr[order[i]].sort_index = parent_order; + bonesptr[parent_idx].sort_index = i; + //swap order + SWAP(order[i], order[parent_order]); + swapped = true; + } + } + + if (!swapped) + break; + pass_count++; + } + + if (pass_count == len * len) { + ERR_PRINT("Skeleton parenthood graph is cyclic"); + } + + process_order_dirty = false; +} + void Skeleton::_notification(int p_what) { switch (p_what) { @@ -176,24 +229,28 @@ void Skeleton::_notification(int p_what) { case NOTIFICATION_UPDATE_SKELETON: { VisualServer *vs = VisualServer::get_singleton(); - Bone *bonesptr = &bones[0]; + Bone *bonesptr = bones.ptrw(); int len = bones.size(); vs->skeleton_allocate(skeleton, len); // if same size, nothin really happens + _update_process_order(); + + const int *order = process_order.ptr(); + // pose changed, rebuild cache of inverses if (rest_global_inverse_dirty) { // calculate global rests and invert them for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.parent >= 0) b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest; else b.rest_global_inverse = b.rest; } for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; b.rest_global_inverse.affine_invert(); } @@ -205,7 +262,7 @@ void Skeleton::_notification(int p_what) { for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.disable_rest) { if (b.enabled) { @@ -319,12 +376,13 @@ void Skeleton::add_bone(const String &p_name) { for (int i = 0; i < bones.size(); i++) { - ERR_FAIL_COND(bones[i].name == "p_name"); + ERR_FAIL_COND(bones[i].name == p_name); } Bone b; b.name = p_name; bones.push_back(b); + process_order_dirty = true; rest_global_inverse_dirty = true; _make_dirty(); @@ -368,10 +426,11 @@ int Skeleton::get_bone_count() const { void Skeleton::set_bone_parent(int p_bone, int p_parent) { ERR_FAIL_INDEX(p_bone, bones.size()); - ERR_FAIL_COND(p_parent != -1 && (p_parent < 0 || p_parent >= p_bone)); + ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); - bones[p_bone].parent = p_parent; + bones.write[p_bone].parent = p_parent; rest_global_inverse_dirty = true; + process_order_dirty = true; _make_dirty(); } @@ -379,21 +438,24 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); + _update_process_order(); + int parent = bones[p_bone].parent; while (parent >= 0) { - bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest; + bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest; parent = bones[parent].parent; } - bones[p_bone].parent = -1; - bones[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing + bones.write[p_bone].parent = -1; + bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing + process_order_dirty = true; _make_dirty(); } void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].ignore_animation = p_ignore; + bones.write[p_bone].ignore_animation = p_ignore; } bool Skeleton::is_bone_ignore_animation(int p_bone) const { @@ -405,7 +467,7 @@ bool Skeleton::is_bone_ignore_animation(int p_bone) const { void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].disable_rest = p_disable; + bones.write[p_bone].disable_rest = p_disable; } bool Skeleton::is_bone_rest_disabled(int p_bone) const { @@ -425,7 +487,7 @@ void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].rest = p_rest; + bones.write[p_bone].rest = p_rest; rest_global_inverse_dirty = true; _make_dirty(); } @@ -440,7 +502,7 @@ void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].enabled = p_enabled; + bones.write[p_bone].enabled = p_enabled; rest_global_inverse_dirty = true; _make_dirty(); } @@ -457,13 +519,13 @@ void Skeleton::bind_child_node_to_bone(int p_bone, Node *p_node) { uint32_t id = p_node->get_instance_id(); - for (List<uint32_t>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { + for (const List<uint32_t>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { if (E->get() == id) return; // already here } - bones[p_bone].nodes_bound.push_back(id); + bones.write[p_bone].nodes_bound.push_back(id); } void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { @@ -471,7 +533,7 @@ void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { ERR_FAIL_INDEX(p_bone, bones.size()); uint32_t id = p_node->get_instance_id(); - bones[p_bone].nodes_bound.erase(id); + bones.write[p_bone].nodes_bound.erase(id); } void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const { @@ -489,6 +551,8 @@ void Skeleton::clear_bones() { bones.clear(); rest_global_inverse_dirty = true; + process_order_dirty = true; + _make_dirty(); } @@ -499,7 +563,7 @@ void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) { ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(!is_inside_tree()); - bones[p_bone].pose = p_pose; + bones.write[p_bone].pose = p_pose; _make_dirty(); } Transform Skeleton::get_bone_pose(int p_bone) const { @@ -513,8 +577,8 @@ void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) ERR_FAIL_INDEX(p_bone, bones.size()); //ERR_FAIL_COND( !is_inside_scene() ); - bones[p_bone].custom_pose_enable = (p_custom_pose != Transform()); - bones[p_bone].custom_pose = p_custom_pose; + bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); + bones.write[p_bone].custom_pose = p_custom_pose; _make_dirty(); } @@ -538,12 +602,21 @@ void Skeleton::_make_dirty() { dirty = true; } +int Skeleton::get_process_order(int p_idx) { + ERR_FAIL_INDEX_V(p_idx, bones.size(), -1); + _update_process_order(); + return process_order[p_idx]; +} + void Skeleton::localize_rests() { - for (int i = bones.size() - 1; i >= 0; i--) { + _update_process_order(); - if (bones[i].parent >= 0) - set_bone_rest(i, bones[bones[i].parent].rest.affine_inverse() * bones[i].rest); + for (int i = bones.size() - 1; i >= 0; i--) { + int idx = process_order[i]; + if (bones[idx].parent >= 0) { + set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest); + } } } @@ -553,14 +626,14 @@ void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_b ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(bones[p_bone].physical_bone); ERR_FAIL_COND(!p_physical_bone); - bones[p_bone].physical_bone = p_physical_bone; + bones.write[p_bone].physical_bone = p_physical_bone; _rebuild_physical_bones_cache(); } void Skeleton::unbind_physical_bone_from_bone(int p_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].physical_bone = NULL; + bones.write[p_bone].physical_bone = NULL; _rebuild_physical_bones_cache(); } @@ -600,9 +673,12 @@ PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) { void Skeleton::_rebuild_physical_bones_cache() { const int b_size = bones.size(); for (int i = 0; i < b_size; ++i) { - bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i); - if (bones[i].physical_bone) - bones[i].physical_bone->_on_bone_parent_changed(); + PhysicalBone *parent_pb = _get_physical_bone_parent(i); + if (parent_pb != bones[i].physical_bone) { + bones.write[i].cache_parent_physical_bone = parent_pb; + if (bones[i].physical_bone) + bones[i].physical_bone->_on_bone_parent_changed(); + } } } @@ -660,7 +736,7 @@ void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) { if (Variant::STRING == p_bones.get(i).get_type()) { int bone_id = find_bone(p_bones.get(i)); if (bone_id != -1) - sim_bones[c++] = bone_id; + sim_bones.write[c++] = bone_id; } } sim_bones.resize(c); @@ -740,6 +816,8 @@ void Skeleton::_bind_methods() { #endif // _3D_DISABLED + ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } @@ -747,6 +825,7 @@ Skeleton::Skeleton() { rest_global_inverse_dirty = true; dirty = false; + process_order_dirty = true; skeleton = VisualServer::get_singleton()->skeleton_create(); set_notify_transform(true); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 9672acb57a..e044e08437 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -39,6 +39,8 @@ */ #ifndef _3D_DISABLED +typedef int BoneId; + class PhysicalBone; #endif // _3D_DISABLED @@ -52,6 +54,7 @@ class Skeleton : public Spatial { bool enabled; int parent; + int sort_index; //used for re-sorting process order bool ignore_animation; @@ -90,13 +93,15 @@ class Skeleton : public Spatial { bool rest_global_inverse_dirty; Vector<Bone> bones; + Vector<int> process_order; + bool process_order_dirty; RID skeleton; void _make_dirty(); bool dirty; - //bind helpers + // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { Array bound; @@ -110,6 +115,8 @@ class Skeleton : public Spatial { return bound; } + void _update_process_order(); + protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); @@ -170,6 +177,7 @@ public: Transform get_bone_custom_pose(int p_bone) const; void localize_rests(); // used for loaders and tools + int get_process_order(int p_idx); #ifndef _3D_DISABLED // Physical bone API diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp new file mode 100644 index 0000000000..980c348c9b --- /dev/null +++ b/scene/3d/soft_body.cpp @@ -0,0 +1,799 @@ +/*************************************************************************/ +/* soft_physics_body.cpp */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "soft_body.h" +#include "os/os.h" +#include "scene/3d/collision_object.h" +#include "scene/3d/skeleton.h" +#include "servers/physics_server.h" + +SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {} + +void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) { + clear(); + + ERR_FAIL_COND(!p_mesh.is_valid()); + + mesh = p_mesh; + surface = p_surface; + + const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface); + const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface); + const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface); + uint32_t surface_offsets[VS::ARRAY_MAX]; + + buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface); + stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets); + offset_vertices = surface_offsets[VS::ARRAY_VERTEX]; + offset_normal = surface_offsets[VS::ARRAY_NORMAL]; +} + +void SoftBodyVisualServerHandler::clear() { + + if (mesh.is_valid()) { + buffer.resize(0); + } + + mesh = RID(); +} + +void SoftBodyVisualServerHandler::open() { + write_buffer = buffer.write(); +} + +void SoftBodyVisualServerHandler::close() { + write_buffer = PoolVector<uint8_t>::Write(); +} + +void SoftBodyVisualServerHandler::commit_changes() { + VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer); +} + +void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) { + VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb); +} + +SoftBody::PinnedPoint::PinnedPoint() : + point_index(-1), + spatial_attachment(NULL) { +} + +SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { + point_index = obj_tocopy.point_index; + spatial_attachment_path = obj_tocopy.spatial_attachment_path; + spatial_attachment = obj_tocopy.spatial_attachment; + offset = obj_tocopy.offset; +} + +void SoftBody::_update_pickable() { + if (!is_inside_tree()) + return; + bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree(); + PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable); +} + +bool SoftBody::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + + return _set_property_pinned_points_indices(p_value); + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _set_property_pinned_points_attachment(idx, what, p_value); + } + + return false; +} + +bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + Array arr_ret; + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + arr_ret.resize(pinned_points_indices_size); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + arr_ret[i] = r[i].point_index; + } + + r_ret = arr_ret; + return true; + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _get_property_pinned_points(idx, what, r_ret); + } + + return false; +} + +void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const { + + const int pinned_points_indices_size = pinned_points.size(); + + p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points")); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); + p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); + } +} + +bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { + + const int p_indices_size = p_indices.size(); + + { // Remove the pined points on physics server that will be removed by resize + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + if (p_indices_size < pinned_points.size()) { + for (int i = pinned_points.size() - 1; i >= p_indices_size; --i) { + pin_point(r[i].point_index, false); + } + } + } + + pinned_points.resize(p_indices_size); + + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + int point_index; + for (int i = 0; i < p_indices_size; ++i) { + point_index = p_indices.get(i); + if (w[i].point_index != point_index) { + if (-1 != w[i].point_index) + pin_point(w[i].point_index, false); + w[i].point_index = point_index; + pin_point(w[i].point_index, true); + } + } + return true; +} + +bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { + if (pinned_points.size() <= p_item) { + return false; + } + + if ("spatial_attachment_path" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } else if ("offset" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + w[p_item].offset = p_value; + } else { + return false; + } + + return true; +} + +bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { + if (pinned_points.size() <= p_item) { + return false; + } + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + + if ("point_index" == p_what) { + r_ret = r[p_item].point_index; + } else if ("spatial_attachment_path" == p_what) { + r_ret = r[p_item].spatial_attachment_path; + } else if ("offset" == p_what) { + r_ret = r[p_item].offset; + } else { + return false; + } + + return true; +} + +void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) { + update_physics_server(); + _reset_points_offsets(); +#ifdef TOOLS_ENABLED + if (p_changed == this) { + update_configuration_warning(); + } +#endif +} + +void SoftBody::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_WORLD: { + + if (Engine::get_singleton()->is_editor_hint()) { + + add_change_receptor(this); + } + + RID space = get_world()->get_space(); + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space); + update_physics_server(); + } break; + case NOTIFICATION_READY: { + if (!parent_collision_ignore.is_empty()) + add_collision_exception_with(get_node(parent_collision_ignore)); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (Engine::get_singleton()->is_editor_hint()) { + _reset_points_offsets(); + return; + } + + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + + set_notify_transform(false); + // Required to be top level with Transform at center of world in order to modify VisualServer only to support custom Transform + set_as_toplevel(true); + set_transform(Transform()); + set_notify_transform(true); + + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + + if (!simulation_started) + return; + + _update_cache_pin_points_datas(); + // Submit bone attachment + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = 0; i < pinned_points_indices_size; ++i) { + if (r[i].spatial_attachment) { + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, r[i].spatial_attachment->get_global_transform().xform(r[i].offset)); + } + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + + _update_pickable(); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID()); + + } break; + } + +#ifdef TOOLS_ENABLED + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warning(); + } + } + +#endif +} + +void SoftBody::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit); + + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit); + + ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore); + + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with); + + ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision); + ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision); + + ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass); + ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass); + + ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness); + ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness); + + ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness); + ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness); + + ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness); + ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness); + + ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient); + ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient); + + ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient); + ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient); + + ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient); + ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient); + + ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient); + + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable); + ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable); + + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient"); +} + +String SoftBody::get_configuration_warning() const { + + String warning = MeshInstance::get_configuration_warning(); + + if (get_mesh().is_null()) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("This body will be ignored until you set a mesh"); + } + + Transform t = get_transform(); + if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + } + + return warning; +} + +void SoftBody::_draw_soft_mesh() { + if (get_mesh().is_null()) + return; + + if (!visual_server_handler.is_ready()) { + + visual_server_handler.prepare(get_mesh()->get_rid(), 0); + + /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) + simulation_started = true; + call_deferred("set_as_toplevel", true); + call_deferred("set_transform", Transform()); + } + + visual_server_handler.open(); + PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler); + visual_server_handler.close(); + + visual_server_handler.commit_changes(); +} + +void SoftBody::update_physics_server() { + + if (Engine::get_singleton()->is_editor_hint()) { + + if (get_mesh().is_valid()) + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + else + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + + return; + } + + if (get_mesh().is_valid()) { + + become_mesh_owner(); + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh"); + } else { + + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh"); + } +} + +void SoftBody::become_mesh_owner() { + if (mesh.is_null()) + return; + + if (!mesh_owner) { + mesh_owner = true; + + Vector<Ref<Material> > copy_materials; + copy_materials.append_array(materials); + + ERR_FAIL_COND(!mesh->get_surface_count()); + + // Get current mesh array and create new mesh array with necessary flag for softbody + Array surface_arrays = mesh->surface_get_arrays(0); + Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0); + uint32_t surface_format = mesh->surface_get_format(0); + + surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL); + surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; + + Ref<ArrayMesh> soft_mesh; + soft_mesh.instance(); + soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format); + soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); + + set_mesh(soft_mesh); + + for (int i = copy_materials.size() - 1; 0 <= i; --i) { + set_surface_material(i, copy_materials[i]); + } + } +} + +void SoftBody::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask); +} + +uint32_t SoftBody::get_collision_mask() const { + return collision_mask; +} +void SoftBody::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer); +} + +uint32_t SoftBody::get_collision_layer() const { + return collision_layer; +} + +void SoftBody::set_collision_mask_bit(int p_bit, bool p_value) { + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool SoftBody::get_collision_mask_bit(int p_bit) const { + return get_collision_mask() & (1 << p_bit); +} + +void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) { + uint32_t layer = get_collision_layer(); + if (p_value) + layer |= 1 << p_bit; + else + layer &= ~(1 << p_bit); + set_collision_layer(layer); +} + +bool SoftBody::get_collision_layer_bit(int p_bit) const { + return get_collision_layer() & (1 << p_bit); +} + +void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { + parent_collision_ignore = p_parent_collision_ignore; +} + +const NodePath &SoftBody::get_parent_collision_ignore() const { + return parent_collision_ignore; +} + +void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) { + pinned_points = p_pinned_points_indices; + PoolVector<PinnedPoint>::Read w = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + pin_point(p_pinned_points_indices[i].point_index, true); + } +} + +PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { + return pinned_points; +} + +void SoftBody::add_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); +} + +void SoftBody::remove_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); +} + +int SoftBody::get_simulation_precision() { + return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid); +} + +void SoftBody::set_simulation_precision(int p_simulation_precision) { + PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision); +} + +real_t SoftBody::get_total_mass() { + return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid); +} + +void SoftBody::set_total_mass(real_t p_total_mass) { + PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass); +} + +void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness); +} + +real_t SoftBody::get_linear_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid); +} + +void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness); +} + +real_t SoftBody::get_areaAngular_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid); +} + +void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness); +} + +real_t SoftBody::get_volume_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid); +} + +real_t SoftBody::get_pressure_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); +} + +void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient); +} + +real_t SoftBody::get_pose_matching_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid); +} + +void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); +} + +real_t SoftBody::get_damping_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid); +} + +void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient); +} + +real_t SoftBody::get_drag_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid); +} + +void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient); +} + +Vector3 SoftBody::get_point_transform(int p_point_index) { + return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); +} + +void SoftBody::pin_point_toggle(int p_point_index) { + pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); +} + +void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { + _pin_point_on_physics_server(p_point_index, pin); + if (pin) { + _add_pinned_point(p_point_index, p_spatial_attachment_path); + } else { + _remove_pinned_point(p_point_index); + } +} + +bool SoftBody::is_point_pinned(int p_point_index) const { + return -1 != _has_pinned_point(p_point_index); +} + +void SoftBody::set_ray_pickable(bool p_ray_pickable) { + + ray_pickable = p_ray_pickable; + _update_pickable(); +} + +bool SoftBody::is_ray_pickable() const { + + return ray_pickable; +} + +SoftBody::SoftBody() : + MeshInstance(), + physics_rid(PhysicsServer::get_singleton()->soft_body_create()), + mesh_owner(false), + collision_mask(1), + collision_layer(1), + simulation_started(false), + pinned_points_cache_dirty(true) { + + PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); + //set_notify_transform(true); + set_physics_process_internal(true); +} + +SoftBody::~SoftBody() { +} + +void SoftBody::reset_softbody_pin() { + PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); + PoolVector<PinnedPoint>::Read pps = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 < i; --i) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); + } +} + +void SoftBody::_make_cache_dirty() { + pinned_points_cache_dirty = true; +} + +void SoftBody::_update_cache_pin_points_datas() { + if (!pinned_points_cache_dirty) + return; + + pinned_points_cache_dirty = false; + + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!w[i].spatial_attachment_path.is_empty()) { + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); + } + if (!w[i].spatial_attachment) { + ERR_PRINT("Spatial node not defined in the pinned point, Softbody undefined behaviour!"); + } + } +} + +void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); +} + +void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { + SoftBody::PinnedPoint *pinned_point; + if (-1 == _get_pinned_point(p_point_index, pinned_point)) { + + // Create new + PinnedPoint pp; + pp.point_index = p_point_index; + pp.spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pp.spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); + } + + pinned_points.push_back(pp); + + } else { + + pinned_point->point_index = p_point_index; + pinned_point->spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pinned_point->spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index)); + } + } +} + +void SoftBody::_reset_points_offsets() { + + if (!Engine::get_singleton()->is_editor_hint()) + return; + + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!r[i].spatial_attachment) + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(r[i].spatial_attachment_path)); + + if (!r[i].spatial_attachment) + continue; + + w[i].offset = (r[i].spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, r[i].point_index)); + } +} + +void SoftBody::_remove_pinned_point(int p_point_index) { + const int id(_has_pinned_point(p_point_index)); + if (-1 != id) { + pinned_points.remove(id); + } +} + +int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const { + const int id = _has_pinned_point(p_point_index); + if (-1 == id) { + r_point = NULL; + return -1; + } else { + r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points.read()[id]); + return id; + } +} + +int SoftBody::_has_pinned_point(int p_point_index) const { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + if (p_point_index == r[i].point_index) { + return i; + } + } + return -1; +} diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h new file mode 100644 index 0000000000..cee32b9651 --- /dev/null +++ b/scene/3d/soft_body.h @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* soft_physics_body.h */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SOFT_PHYSICS_BODY_H +#define SOFT_PHYSICS_BODY_H + +#include "scene/3d/mesh_instance.h" + +class SoftBody; + +class SoftBodyVisualServerHandler { + + friend class SoftBody; + + RID mesh; + int surface; + PoolVector<uint8_t> buffer; + uint32_t stride; + uint32_t offset_vertices; + uint32_t offset_normal; + + PoolVector<uint8_t>::Write write_buffer; + +private: + SoftBodyVisualServerHandler(); + bool is_ready() { return mesh.is_valid(); } + void prepare(RID p_mesh_rid, int p_surface); + void clear(); + void open(); + void close(); + void commit_changes(); + +public: + void set_vertex(int p_vertex_id, const void *p_vector3); + void set_normal(int p_vertex_id, const void *p_vector3); + void set_aabb(const AABB &p_aabb); +}; + +class SoftBody : public MeshInstance { + GDCLASS(SoftBody, MeshInstance); + +public: + struct PinnedPoint { + int point_index; + NodePath spatial_attachment_path; + Spatial *spatial_attachment; // Cache + Vector3 offset; + + PinnedPoint(); + PinnedPoint(const PinnedPoint &obj_tocopy); + }; + +private: + SoftBodyVisualServerHandler visual_server_handler; + + RID physics_rid; + + bool mesh_owner; + uint32_t collision_mask; + uint32_t collision_layer; + NodePath parent_collision_ignore; + PoolVector<PinnedPoint> pinned_points; + bool simulation_started; + bool pinned_points_cache_dirty; + + Ref<ArrayMesh> debug_mesh_cache; + class MeshInstance *debug_mesh; + + bool capture_input_on_drag; + bool ray_pickable; + + void _update_pickable(); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + bool _set_property_pinned_points_indices(const Array &p_indices); + bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value); + bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const; + + virtual void _changed_callback(Object *p_changed, const char *p_prop); + + void _notification(int p_what); + static void _bind_methods(); + + virtual String get_configuration_warning() const; + +protected: + void _draw_soft_mesh(); + +public: + void update_physics_server(); + void become_mesh_owner(); + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore); + const NodePath &get_parent_collision_ignore() const; + + void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices); + PoolVector<PinnedPoint> get_pinned_points_indices(); + + void set_simulation_precision(int p_simulation_precision); + int get_simulation_precision(); + + void set_total_mass(real_t p_total_mass); + real_t get_total_mass(); + + void set_linear_stiffness(real_t p_linear_stiffness); + real_t get_linear_stiffness(); + + void set_areaAngular_stiffness(real_t p_areaAngular_stiffness); + real_t get_areaAngular_stiffness(); + + void set_volume_stiffness(real_t p_volume_stiffness); + real_t get_volume_stiffness(); + + void set_pressure_coefficient(real_t p_pressure_coefficient); + real_t get_pressure_coefficient(); + + void set_pose_matching_coefficient(real_t p_pose_matching_coefficient); + real_t get_pose_matching_coefficient(); + + void set_damping_coefficient(real_t p_damping_coefficient); + real_t get_damping_coefficient(); + + void set_drag_coefficient(real_t p_drag_coefficient); + real_t get_drag_coefficient(); + + void add_collision_exception_with(Node *p_node); + void remove_collision_exception_with(Node *p_node); + + Vector3 get_point_transform(int p_point_index); + + void pin_point_toggle(int p_point_index); + void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + bool is_point_pinned(int p_point_index) const; + + void set_ray_pickable(bool p_ray_pickable); + bool is_ray_pickable() const; + + SoftBody(); + ~SoftBody(); + +private: + void reset_softbody_pin(); + + void _make_cache_dirty(); + void _update_cache_pin_points_datas(); + + void _pin_point_on_physics_server(int p_point_index, bool pin); + void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _reset_points_offsets(); + + void _remove_pinned_point(int p_point_index); + int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const; + int _has_pinned_point(int p_point_index) const; +}; + +#endif // SOFT_PHYSICS_BODY_H diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 9b27faed6a..3f494264e7 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -202,6 +202,7 @@ void Spatial::_notification(int p_what) { #ifdef TOOLS_ENABLED if (data.gizmo.is_valid()) { data.gizmo->free(); + data.gizmo.unref(); } #endif @@ -645,19 +646,15 @@ void Spatial::scale_object_local(const Vector3 &p_scale) { void Spatial::global_rotate(const Vector3 &p_axis, float p_angle) { - Basis rotation(p_axis, p_angle); Transform t = get_global_transform(); - t.basis = rotation * t.basis; + t.basis.rotate(p_axis, p_angle); set_global_transform(t); } void Spatial::global_scale(const Vector3 &p_scale) { - Basis s; - s.set_scale(p_scale); - Transform t = get_global_transform(); - t.basis = s * t.basis; + t.basis.scale(p_scale); set_global_transform(t); } @@ -797,13 +794,13 @@ void Spatial::_bind_methods() { //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ; ADD_GROUP("Transform", ""); - ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_translation", "get_translation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); - + ADD_GROUP("Matrix", ""); + ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo"); diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index 653714dc98..bc054a8763 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -51,6 +51,7 @@ public: virtual bool can_draw() const = 0; SpatialGizmo(); + virtual ~SpatialGizmo() {} }; class Spatial : public Node { diff --git a/scene/3d/spatial_velocity_tracker.cpp b/scene/3d/spatial_velocity_tracker.cpp index c547e76e30..d96b003a81 100644 --- a/scene/3d/spatial_velocity_tracker.cpp +++ b/scene/3d/spatial_velocity_tracker.cpp @@ -53,11 +53,11 @@ void SpatialVelocityTracker::update_position(const Vector3 &p_position) { if (position_history_len == 0 || position_history[0].frame != ph.frame) { //in same frame, use latest position_history_len = MIN(position_history.size(), position_history_len + 1); for (int i = position_history_len - 1; i > 0; i--) { - position_history[i] = position_history[i - 1]; + position_history.write[i] = position_history[i - 1]; } } - position_history[0] = ph; + position_history.write[0] = ph; } Vector3 SpatialVelocityTracker::get_tracked_linear_velocity() const { @@ -114,7 +114,7 @@ void SpatialVelocityTracker::reset(const Vector3 &p_new_pos) { ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); } - position_history[0] = ph; + position_history.write[0] = ph; position_history_len = 1; } diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index 385956dc16..26958930e4 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) { //bilateral constraint between two dynamic objects void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, - PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) { + PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence) { real_t normalLenSqr = normal.length_squared(); //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); @@ -677,8 +677,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { //collapse all those loops into one! for (int i = 0; i < wheels.size(); i++) { - m_sideImpulse[i] = real_t(0.); - m_forwardImpulse[i] = real_t(0.); + m_sideImpulse.write[i] = real_t(0.); + m_forwardImpulse.write[i] = real_t(0.); } { @@ -693,22 +693,22 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; - m_axle[i] = wheelBasis0.get_axis(Vector3::AXIS_X); + m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; real_t proj = m_axle[i].dot(surfNormalWS); - m_axle[i] -= surfNormalWS * proj; - m_axle[i] = m_axle[i].normalized(); + m_axle.write[i] -= surfNormalWS * proj; + m_axle.write[i] = m_axle[i].normalized(); - m_forwardWS[i] = surfNormalWS.cross(m_axle[i]); - m_forwardWS[i].normalize(); + m_forwardWS.write[i] = surfNormalWS.cross(m_axle[i]); + m_forwardWS.write[i].normalize(); _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, - m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence); + m_axle[i], m_sideImpulse.write[i], wheelInfo.m_rollInfluence); - m_sideImpulse[i] *= sideFrictionStiffness2; + m_sideImpulse.write[i] *= sideFrictionStiffness2; } } } @@ -739,7 +739,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break) - m_forwardImpulse[wheel] = real_t(0.); + m_forwardImpulse.write[wheel] = real_t(0.); wheelInfo.m_skidInfo = real_t(1.); if (wheelInfo.m_raycastInfo.m_isInContact) { @@ -750,7 +750,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { real_t maximpSquared = maximp * maximpSide; - m_forwardImpulse[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; + m_forwardImpulse.write[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; real_t x = (m_forwardImpulse[wheel]) * fwdFactor; real_t y = (m_sideImpulse[wheel]) * sideFactor; @@ -772,8 +772,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { for (int wheel = 0; wheel < wheels.size(); wheel++) { if (m_sideImpulse[wheel] != real_t(0.)) { if (wheels[wheel]->m_skidInfo < real_t(1.)) { - m_forwardImpulse[wheel] *= wheels[wheel]->m_skidInfo; - m_sideImpulse[wheel] *= wheels[wheel]->m_skidInfo; + m_forwardImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; + m_sideImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; } } } @@ -942,8 +942,6 @@ VehicleBody::VehicleBody() : engine_force = 0; brake = 0; - friction = 1; - state = NULL; ccd = false; diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index 1ac3693cc4..68fbf8d873 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -168,7 +168,7 @@ class VehicleBody : public RigidBody { btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); }; - void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence); + void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence); real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); void _update_friction(PhysicsDirectBodyState *s); diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 00541a7d8a..767518dc83 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -105,12 +105,28 @@ uint32_t VisualInstance::get_layer_mask() const { return layers; } +void VisualInstance::set_layer_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_layer_mask(layers | (1 << p_layer)); + } else { + set_layer_mask(layers & (~(1 << p_layer))); + } +} + +bool VisualInstance::get_layer_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + void VisualInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid); ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask); + ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit); + ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance::get_layer_mask_bit); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance::get_transformed_aabb); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 8458a343b2..9249bc04ce 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -73,6 +73,9 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; + void set_layer_mask_bit(int p_layer, bool p_enable); + bool get_layer_mask_bit(int p_layer) const; + VisualInstance(); ~VisualInstance(); }; diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index ba2807d4e8..f3abdc6bbe 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -409,16 +409,16 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p } //put this temporarily here, corrected in a later step - bake_cells[p_idx].albedo[0] += albedo_accum.r; - bake_cells[p_idx].albedo[1] += albedo_accum.g; - bake_cells[p_idx].albedo[2] += albedo_accum.b; - bake_cells[p_idx].emission[0] += emission_accum.r; - bake_cells[p_idx].emission[1] += emission_accum.g; - bake_cells[p_idx].emission[2] += emission_accum.b; - bake_cells[p_idx].normal[0] += normal_accum.x; - bake_cells[p_idx].normal[1] += normal_accum.y; - bake_cells[p_idx].normal[2] += normal_accum.z; - bake_cells[p_idx].alpha += alpha; + bake_cells.write[p_idx].albedo[0] += albedo_accum.r; + bake_cells.write[p_idx].albedo[1] += albedo_accum.g; + bake_cells.write[p_idx].albedo[2] += albedo_accum.b; + bake_cells.write[p_idx].emission[0] += emission_accum.r; + bake_cells.write[p_idx].emission[1] += emission_accum.g; + bake_cells.write[p_idx].emission[2] += emission_accum.b; + bake_cells.write[p_idx].normal[0] += normal_accum.x; + bake_cells.write[p_idx].normal[1] += normal_accum.y; + bake_cells.write[p_idx].normal[2] += normal_accum.z; + bake_cells.write[p_idx].alpha += alpha; } else { //go down @@ -465,9 +465,9 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p //sub cell must be created uint32_t child_idx = bake_cells.size(); - bake_cells[p_idx].children[i] = child_idx; + bake_cells.write[p_idx].children[i] = child_idx; bake_cells.resize(bake_cells.size() + 1); - bake_cells[child_idx].level = p_level + 1; + bake_cells.write[child_idx].level = p_level + 1; } _plot_face(bake_cells[p_idx].children[i], p_level + 1, nx, ny, nz, p_vtx, p_normal, p_uv, p_material, aabb); @@ -483,7 +483,7 @@ Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color ret.resize(bake_texture_size * bake_texture_size); for (int i = 0; i < bake_texture_size * bake_texture_size; i++) { - ret[i] = p_color_add; + ret.write[i] = p_color_add; } return ret; @@ -509,7 +509,7 @@ Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color c.a = r[i * 4 + 3] / 255.0; - ret[i] = c; + ret.write[i] = c; } return ret; @@ -686,13 +686,13 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con void VoxelLightBaker::_init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent) { - bake_light[p_idx].x = p_x; - bake_light[p_idx].y = p_y; - bake_light[p_idx].z = p_z; + bake_light.write[p_idx].x = p_x; + bake_light.write[p_idx].y = p_y; + bake_light.write[p_idx].z = p_z; if (p_level == cell_subdiv - 1) { - bake_light[p_idx].next_leaf = first_leaf; + bake_light.write[p_idx].next_leaf = first_leaf; first_leaf = p_idx; } else { @@ -1197,33 +1197,33 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { leaf_voxel_count++; float alpha = bake_cells[p_idx].alpha; - bake_cells[p_idx].albedo[0] /= alpha; - bake_cells[p_idx].albedo[1] /= alpha; - bake_cells[p_idx].albedo[2] /= alpha; + bake_cells.write[p_idx].albedo[0] /= alpha; + bake_cells.write[p_idx].albedo[1] /= alpha; + bake_cells.write[p_idx].albedo[2] /= alpha; //transfer emission to light - bake_cells[p_idx].emission[0] /= alpha; - bake_cells[p_idx].emission[1] /= alpha; - bake_cells[p_idx].emission[2] /= alpha; + bake_cells.write[p_idx].emission[0] /= alpha; + bake_cells.write[p_idx].emission[1] /= alpha; + bake_cells.write[p_idx].emission[2] /= alpha; - bake_cells[p_idx].normal[0] /= alpha; - bake_cells[p_idx].normal[1] /= alpha; - bake_cells[p_idx].normal[2] /= alpha; + bake_cells.write[p_idx].normal[0] /= alpha; + bake_cells.write[p_idx].normal[1] /= alpha; + bake_cells.write[p_idx].normal[2] /= alpha; Vector3 n(bake_cells[p_idx].normal[0], bake_cells[p_idx].normal[1], bake_cells[p_idx].normal[2]); if (n.length() < 0.01) { //too much fight over normal, zero it - bake_cells[p_idx].normal[0] = 0; - bake_cells[p_idx].normal[1] = 0; - bake_cells[p_idx].normal[2] = 0; + bake_cells.write[p_idx].normal[0] = 0; + bake_cells.write[p_idx].normal[1] = 0; + bake_cells.write[p_idx].normal[2] = 0; } else { n.normalize(); - bake_cells[p_idx].normal[0] = n.x; - bake_cells[p_idx].normal[1] = n.y; - bake_cells[p_idx].normal[2] = n.z; + bake_cells.write[p_idx].normal[0] = n.x; + bake_cells.write[p_idx].normal[1] = n.y; + bake_cells.write[p_idx].normal[2] = n.z; } - bake_cells[p_idx].alpha = 1.0; + bake_cells.write[p_idx].alpha = 1.0; /*if (bake_light.size()) { for(int i=0;i<6;i++) { @@ -1235,20 +1235,20 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { //go down - bake_cells[p_idx].emission[0] = 0; - bake_cells[p_idx].emission[1] = 0; - bake_cells[p_idx].emission[2] = 0; - bake_cells[p_idx].normal[0] = 0; - bake_cells[p_idx].normal[1] = 0; - bake_cells[p_idx].normal[2] = 0; - bake_cells[p_idx].albedo[0] = 0; - bake_cells[p_idx].albedo[1] = 0; - bake_cells[p_idx].albedo[2] = 0; + bake_cells.write[p_idx].emission[0] = 0; + bake_cells.write[p_idx].emission[1] = 0; + bake_cells.write[p_idx].emission[2] = 0; + bake_cells.write[p_idx].normal[0] = 0; + bake_cells.write[p_idx].normal[1] = 0; + bake_cells.write[p_idx].normal[2] = 0; + bake_cells.write[p_idx].albedo[0] = 0; + bake_cells.write[p_idx].albedo[1] = 0; + bake_cells.write[p_idx].albedo[2] = 0; if (bake_light.size()) { for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] = 0; - bake_light[p_idx].accum[j][1] = 0; - bake_light[p_idx].accum[j][2] = 0; + bake_light.write[p_idx].accum[j][0] = 0; + bake_light.write[p_idx].accum[j][1] = 0; + bake_light.write[p_idx].accum[j][2] = 0; } } @@ -1267,29 +1267,29 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { if (bake_light.size() > 0) { for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] += bake_light[child].accum[j][0]; - bake_light[p_idx].accum[j][1] += bake_light[child].accum[j][1]; - bake_light[p_idx].accum[j][2] += bake_light[child].accum[j][2]; + bake_light.write[p_idx].accum[j][0] += bake_light[child].accum[j][0]; + bake_light.write[p_idx].accum[j][1] += bake_light[child].accum[j][1]; + bake_light.write[p_idx].accum[j][2] += bake_light[child].accum[j][2]; } - bake_cells[p_idx].emission[0] += bake_cells[child].emission[0]; - bake_cells[p_idx].emission[1] += bake_cells[child].emission[1]; - bake_cells[p_idx].emission[2] += bake_cells[child].emission[2]; + bake_cells.write[p_idx].emission[0] += bake_cells[child].emission[0]; + bake_cells.write[p_idx].emission[1] += bake_cells[child].emission[1]; + bake_cells.write[p_idx].emission[2] += bake_cells[child].emission[2]; } children_found++; } - bake_cells[p_idx].alpha = alpha_average / 8.0; + bake_cells.write[p_idx].alpha = alpha_average / 8.0; if (bake_light.size() && children_found) { float divisor = Math::lerp(8, children_found, propagation); for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] /= divisor; - bake_light[p_idx].accum[j][1] /= divisor; - bake_light[p_idx].accum[j][2] /= divisor; + bake_light.write[p_idx].accum[j][0] /= divisor; + bake_light.write[p_idx].accum[j][1] /= divisor; + bake_light.write[p_idx].accum[j][2] /= divisor; } - bake_cells[p_idx].emission[0] /= divisor; - bake_cells[p_idx].emission[1] /= divisor; - bake_cells[p_idx].emission[2] /= divisor; + bake_cells.write[p_idx].emission[0] /= divisor; + bake_cells.write[p_idx].emission[1] /= divisor; + bake_cells.write[p_idx].emission[2] /= divisor; } } } @@ -2403,25 +2403,25 @@ PoolVector<uint8_t> VoxelLightBaker::create_capture_octree(int p_subdiv) { new_size++; demap.push_back(i); } - remap[i] = c; + remap.write[i] = c; } Vector<VoxelLightBakerOctree> octree; octree.resize(new_size); for (int i = 0; i < new_size; i++) { - octree[i].alpha = bake_cells[demap[i]].alpha; + octree.write[i].alpha = bake_cells[demap[i]].alpha; for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { float l = bake_light[demap[i]].accum[j][k]; //add anisotropic light l += bake_cells[demap[i]].emission[k]; //add emission - octree[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree + octree.write[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree } } for (int j = 0; j < 8; j++) { uint32_t child = bake_cells[demap[i]].children[j]; - octree[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child]; + octree.write[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child]; } } diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index bba25d64d9..3c93a0c8ec 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -24,7 +24,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ for (int i = 0; i < triangles.size(); i++) { for (int j = 0; j < 3; j++) { if (triangles[i].points[j] >= p_at_index) { - triangles[i].points[j]++; + triangles.write[i].points[j]++; } } } @@ -81,7 +81,7 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { erase = true; break; } else if (triangles[i].points[j] > p_point) { - triangles[i].points[j]--; + triangles.write[i].points[j]--; } } if (erase) { @@ -264,9 +264,9 @@ Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const { t.resize(triangles.size() * 3); for (int i = 0; i < triangles.size(); i++) { - t[i * 3 + 0] = triangles[i].points[0]; - t[i * 3 + 1] = triangles[i].points[1]; - t[i * 3 + 2] = triangles[i].points[2]; + t.write[i * 3 + 0] = triangles[i].points[0]; + t.write[i * 3 + 1] = triangles[i].points[1]; + t.write[i * 3 + 2] = triangles[i].points[2]; } return t; } @@ -284,7 +284,7 @@ void AnimationNodeBlendSpace2D::_update_triangles() { Vector<Vector2> points; points.resize(blend_points_used); for (int i = 0; i < blend_points_used; i++) { - points[i] = blend_points[i].position; + points.write[i] = blend_points[i].position; } Vector<Delaunay2D::Triangle> triangles = Delaunay2D::triangulate(points); diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index 949a0be3bc..756907c41c 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -43,7 +43,7 @@ void AnimationCache::_node_exit_tree(Node *p_node) { if (path_cache[i].node != p_node) continue; - path_cache[i].valid = false; //invalidate path cache + path_cache.write[i].valid = false; //invalidate path cache } } @@ -196,7 +196,7 @@ void AnimationCache::set_track_transform(int p_idx, const Transform &p_transform ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -217,7 +217,7 @@ void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -232,7 +232,7 @@ void AnimationCache::call_track(int p_idx, const StringName &p_method, const Var ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -297,11 +297,11 @@ void AnimationCache::set_all(float p_time, float p_delta) { call_track(i, name, NULL, 0, err); } else { - Vector<Variant *> argptrs; + Vector<const Variant *> argptrs; argptrs.resize(args.size()); for (int j = 0; j < args.size(); j++) { - argptrs[j] = &args[j]; + argptrs.write[j] = &args.write[j]; } call_track(i, name, (const Variant **)&argptrs[0], args.size(), err); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 36587a1e91..f478112a36 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -169,11 +169,11 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri for (int i = 0; i < transitions.size(); i++) { if (transitions[i].from == p_name) { - transitions[i].from = p_new_name; + transitions.write[i].from = p_new_name; } if (transitions[i].to == p_name) { - transitions[i].to = p_new_name; + transitions.write[i].to = p_new_name; } } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 111620dac1..2782354432 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -233,7 +233,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { for (int i = 0; i < a->get_track_count(); i++) { - p_anim->node_cache[i] = NULL; + p_anim->node_cache.write[i] = NULL; RES resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); @@ -265,12 +265,12 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (node_cache_map.has(key)) { - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; } else { node_cache_map[key] = TrackNodeCache(); - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; p_anim->node_cache[i]->path = a->track_get_path(i); p_anim->node_cache[i]->node = child; p_anim->node_cache[i]->resource = resource; @@ -331,11 +331,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::BezierAnim ba; - String path = leftover_path[leftover_path.size() - 1]; - Vector<String> index = path.split("."); - for (int j = 0; j < index.size(); j++) { - ba.bezier_property.push_back(index[j]); - } + ba.bezier_property = leftover_path; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.owner = p_anim->node_cache[i]; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 4fa66e8ede..de9f82dadc 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -78,9 +78,9 @@ float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p return 0; } - inputs[p_input].last_pass = state->last_pass; + inputs.write[p_input].last_pass = state->last_pass; - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs[p_input].activity); + return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); } float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { @@ -221,7 +221,7 @@ StringName AnimationNode::get_input_connection(int p_input) { void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { ERR_FAIL_INDEX(p_input, inputs.size()); - inputs[p_input].connected_to = p_connection; + inputs.write[p_input].connected_to = p_connection; } String AnimationNode::get_caption() const { @@ -248,7 +248,7 @@ void AnimationNode::add_input(const String &p_name) { void AnimationNode::set_input_name(int p_input, const String &p_name) { ERR_FAIL_INDEX(p_input, inputs.size()); ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); - inputs[p_input].name = p_name; + inputs.write[p_input].name = p_name; emit_changed(); } diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 026215508b..179f5d9698 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -1152,7 +1152,7 @@ void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringNam GET_NODE(NODE_TRANSITION, TransitionNode); ERR_FAIL_INDEX(p_input, n->input_data.size()); - n->input_data[p_input].auto_advance = p_auto_advance; + n->input_data.write[p_input].auto_advance = p_auto_advance; } void AnimationTreePlayer::transition_node_set_xfade_time(const StringName &p_node, float p_time) { @@ -1365,7 +1365,7 @@ void AnimationTreePlayer::remove_node(const StringName &p_node) { for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } @@ -1426,11 +1426,11 @@ Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const Str for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_src_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } - dst->inputs[p_dst_input].node = p_src_node; + dst->inputs.write[p_dst_input].node = p_src_node; _clear_cycle_test(); @@ -1463,7 +1463,7 @@ void AnimationTreePlayer::disconnect_nodes(const StringName &p_node, int p_input NodeBase *dst = node_map[p_node]; ERR_FAIL_INDEX(p_input, dst->inputs.size()); - dst->inputs[p_input].node = StringName(); + dst->inputs.write[p_input].node = StringName(); last_error = CONNECT_INCOMPLETE; dirty_caches = true; } @@ -1703,7 +1703,7 @@ Error AnimationTreePlayer::node_rename(const StringName &p_node, const StringNam for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) { - nb->inputs[i].node = p_new_name; + nb->inputs.write[i].node = p_new_name; } } } diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp new file mode 100644 index 0000000000..4991cedfab --- /dev/null +++ b/scene/animation/skeleton_ik.cpp @@ -0,0 +1,551 @@ +/*************************************************************************/ +/* skeleton_ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik.h" + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = childs.size() - 1; 0 <= i; --i) { + if (p_bone_id == childs[i].bone) { + return &childs.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = childs.size(); + childs.resize(infant_child_id + 1); + childs.write[infant_child_id].bone = p_bone_id; + childs.write[infant_child_id].parent_item = this; + return &childs.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND(-1 == p_task->root_bone); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector<BoneId> chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND(p_task->root_bone >= ee->tip_bone); + ERR_FAIL_INDEX(ee->tip_bone, p_task->skeleton->get_bone_count()); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } +} + +void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->childs.write[i]); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->childs.empty()) { + + ChainItem &child(sub_chain_root->childs.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + build_chain(task); + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->childs.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + + if (!ci->childs.empty()) + ci = &ci->childs.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to<Skeleton>(get_parent()); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK::SkeletonIK() : + Node(), + interpolation(1), + skeleton(NULL), + target_node_override(NULL), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + task(NULL) { + + set_process_priority(1); +} + +SkeletonIK::~SkeletonIK() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK::get_root_bone() const { + return root_bone; +} + +void SkeletonIK::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK::get_interpolation() const { + return interpolation; +} + +void SkeletonIK::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK::get_target_transform() const { + return target; +} + +void SkeletonIK::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK::is_running() { + return is_processing_internal(); +} + +void SkeletonIK::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK::stop() { + set_process_internal(false); +} + +Transform SkeletonIK::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + task->max_iterations = max_iterations; + task->min_distance = min_distance; +} + +void SkeletonIK::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); +} diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h new file mode 100644 index 0000000000..366c599c01 --- /dev/null +++ b/scene/animation/skeleton_ik.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* skeleton_ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector<ChainItem> childs; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector<ChainTip> tips; + Vector3 magnet_position; + }; + +public: + struct Task : public RID_Data { + RID self; + Skeleton *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector<EndEffector> end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static void build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK : public Node { + GDCLASS(SkeletonIK, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton *skeleton; + Spatial *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_notification); + +public: + SkeletonIK(); + virtual ~SkeletonIK(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_constraint); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // SKELETON_IK_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 9f7503577b..81fdc32788 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -150,7 +150,7 @@ void Tween::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - if (!processing) { + if (!is_active()) { //make sure that a previous process state was not saved //only process if "processing" is set set_physics_process_internal(false); @@ -164,7 +164,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_PHYSICS) break; - if (processing) + if (is_active()) _tween_process(get_process_delta_time()); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -172,7 +172,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_IDLE) break; - if (processing) + if (is_active()) _tween_process(get_physics_process_delta_time()); } break; case NOTIFICATION_EXIT_TREE: { @@ -201,7 +201,6 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all); ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL("")); ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all); - ClassDB::bind_method(D_METHOD("is_stopped"), &Tween::is_stopped); ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL("")); ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all); ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL("")); @@ -522,8 +521,8 @@ void Tween::_tween_process(float p_delta) { pending_update++; // if repeat and all interpolates was finished then reset all interpolates + bool all_finished = true; if (repeat) { - bool all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -539,9 +538,12 @@ void Tween::_tween_process(float p_delta) { reset_all(); } + all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { InterpolateData &data = E->get(); + all_finished = all_finished && data.finish; + if (!data.active || data.finish) continue; @@ -555,8 +557,8 @@ void Tween::_tween_process(float p_delta) { continue; else if (prev_delaying) { - emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); _apply_tween_value(data, data.initial_val); + emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); } if (data.elapsed > (data.delay + data.duration)) { @@ -603,32 +605,29 @@ void Tween::_tween_process(float p_delta) { } } else { Variant result = _run_equation(data); - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); _apply_tween_value(data, result); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); } if (data.finish) { _apply_tween_value(data, data.final_val); + data.elapsed = 0; emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) call_deferred("_remove", object, NodePath(Vector<StringName>(), data.key, false), true); - } + } else if (!repeat) + all_finished = all_finished && data.finish; } pending_update--; + + if (all_finished) + set_active(false); } void Tween::set_tween_process_mode(TweenProcessMode p_mode) { - if (tween_process_mode == p_mode) - return; - - bool pr = processing; - if (pr) - _set_process(false); tween_process_mode = p_mode; - if (pr) - _set_process(true); } Tween::TweenProcessMode Tween::get_tween_process_mode() const { @@ -636,32 +635,21 @@ Tween::TweenProcessMode Tween::get_tween_process_mode() const { return tween_process_mode; } -void Tween::_set_process(bool p_process, bool p_force) { - - if (processing == p_process && !p_force) - return; - - switch (tween_process_mode) { - - case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; - case TWEEN_PROCESS_IDLE: set_process_internal(p_process && active); break; - } - - processing = p_process; -} - bool Tween::is_active() const { - return active; + return is_processing_internal() || is_physics_processing_internal(); } void Tween::set_active(bool p_active) { - if (active == p_active) + if (is_active() == p_active) return; - active = p_active; - _set_process(processing, true); + switch (tween_process_mode) { + + case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break; + case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break; + } } bool Tween::is_repeat() const { @@ -687,7 +675,6 @@ float Tween::get_speed_scale() const { bool Tween::start() { set_active(true); - _set_process(true); return true; } @@ -744,14 +731,9 @@ bool Tween::stop(Object *p_object, StringName p_key) { return true; } -bool Tween::is_stopped() const { - return tell() >= get_runtime(); -} - bool Tween::stop_all() { set_active(false); - _set_process(false); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -766,7 +748,6 @@ bool Tween::stop_all() { bool Tween::resume(Object *p_object, StringName p_key) { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -785,7 +766,6 @@ bool Tween::resume(Object *p_object, StringName p_key) { bool Tween::resume_all() { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -834,7 +814,6 @@ bool Tween::remove_all() { return true; } set_active(false); - _set_process(false); interpolates.clear(); return true; } @@ -1425,8 +1404,6 @@ Tween::Tween() { //String autoplay; tween_process_mode = TWEEN_PROCESS_IDLE; - processing = false; - active = false; repeat = false; speed_scale = 1; pending_update = 0; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 36094bf294..9997349c64 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -104,8 +104,6 @@ private: String autoplay; TweenProcessMode tween_process_mode; - bool processing; - bool active; bool repeat; float speed_scale; mutable int pending_update; @@ -133,7 +131,6 @@ private: bool _apply_tween_value(InterpolateData &p_data, Variant &value); void _tween_process(float p_delta); - void _set_process(bool p_process, bool p_force = false); void _remove(Object *p_object, StringName p_key, bool first_only); protected: @@ -162,7 +159,6 @@ public: bool reset_all(); bool stop(Object *p_object, StringName p_key); bool stop_all(); - bool is_stopped() const; bool resume(Object *p_object, StringName p_key); bool resume_all(); bool remove(Object *p_object, StringName p_key); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 17c349858f..18f06eca31 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -663,6 +663,9 @@ void Control::_notification(int p_notification) { bool Control::clips_input() const { + if (get_script_instance()) { + return get_script_instance()->call(SceneStringNames::get_singleton()->_clips_input); + } return false; } bool Control::has_point(const Point2 &p_point) const { @@ -2828,6 +2831,7 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT); @@ -2972,7 +2976,6 @@ Control::Control() { data.SI = NULL; data.MI = NULL; data.RI = NULL; - data.modal = false; data.theme_owner = NULL; data.modal_exclusive = false; data.default_cursor = CURSOR_ARROW; diff --git a/scene/gui/control.h b/scene/gui/control.h index 94231867d7..6bea04345b 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -182,7 +182,6 @@ private: Control *parent; ObjectID drag_owner; - bool modal; bool modal_exclusive; uint64_t modal_frame; //frame used to put something as modal Ref<Theme> theme; diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index b5622604e2..749efe8364 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -256,7 +256,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { if (!valid) return; - points[grabbed].offset = newofs; + points.write[grabbed].offset = newofs; points.sort(); for (int i = 0; i < points.size(); i++) { @@ -407,7 +407,7 @@ void GradientEdit::_color_changed(const Color &p_color) { if (grabbed == -1) return; - points[grabbed].color = p_color; + points.write[grabbed].color = p_color; update(); emit_signal("ramp_changed"); } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index e2c730a56e..d95ec9e495 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -268,6 +268,10 @@ void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal"); + port_grab_distance_vertical = get_constant("port_grab_distance_vertical"); + } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); @@ -343,8 +347,6 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { Ref<Texture> port = get_icon("port", "GraphNode"); - float grab_r_extend = 2.0; - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -354,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) + if (create_hot_zone(pos).has_point(p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) { + if (create_hot_zone(pos).has_point(p_point)) { return true; } } @@ -372,13 +374,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { - float grab_r_extend = 2.0; Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos(mb->get_position().x, mb->get_position().y); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,8 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -492,7 +491,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos = mm->get_position(); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -504,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; @@ -519,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -559,6 +557,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } +Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { + return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +} + template <class Vector2> static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { /* Formula from Wikipedia article on Bezier curves. */ diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 14789001e4..64ba18681e 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -81,6 +81,9 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; + float port_grab_distance_horizontal; + float port_grab_distance_vertical; + bool connecting; String connecting_from; bool connecting_out; @@ -127,6 +130,9 @@ private: Control *connections_layer; GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); + + Rect2 create_hot_zone(const Vector2 &pos); + void _top_layer_draw(); void _connections_layer_draw(); void _update_scroll_offset(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index aa52739b0a..5c79741682 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -72,7 +72,7 @@ void ItemList::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].text = p_text; + items.write[p_idx].text = p_text; update(); shape_changed = true; } @@ -85,7 +85,7 @@ String ItemList::get_item_text(int p_idx) const { void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip_enabled = p_enabled; + items.write[p_idx].tooltip_enabled = p_enabled; } bool ItemList::is_item_tooltip_enabled(int p_idx) const { @@ -97,7 +97,7 @@ void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip = p_tooltip; + items.write[p_idx].tooltip = p_tooltip; update(); shape_changed = true; } @@ -112,7 +112,7 @@ void ItemList::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon = p_icon; + items.write[p_idx].icon = p_icon; update(); shape_changed = true; } @@ -128,7 +128,7 @@ void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon_region = p_region; + items.write[p_idx].icon_region = p_region; update(); shape_changed = true; } @@ -144,7 +144,7 @@ void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon_modulate = p_modulate; + items.write[p_idx].icon_modulate = p_modulate; update(); } @@ -159,7 +159,7 @@ void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_colo ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].custom_bg = p_custom_bg_color; + items.write[p_idx].custom_bg = p_custom_bg_color; } Color ItemList::get_item_custom_bg_color(int p_idx) const { @@ -173,7 +173,7 @@ void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_colo ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].custom_fg = p_custom_fg_color; + items.write[p_idx].custom_fg = p_custom_fg_color; } Color ItemList::get_item_custom_fg_color(int p_idx) const { @@ -187,7 +187,7 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture> &p_tag_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tag_icon = p_tag_icon; + items.write[p_idx].tag_icon = p_tag_icon; update(); shape_changed = true; } @@ -202,7 +202,7 @@ void ItemList::set_item_selectable(int p_idx, bool p_selectable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].selectable = p_selectable; + items.write[p_idx].selectable = p_selectable; } bool ItemList::is_item_selectable(int p_idx) const { @@ -215,7 +215,7 @@ void ItemList::set_item_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].disabled = p_disabled; + items.write[p_idx].disabled = p_disabled; update(); } @@ -229,7 +229,7 @@ void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].metadata = p_metadata; + items.write[p_idx].metadata = p_metadata; update(); shape_changed = true; } @@ -250,7 +250,7 @@ void ItemList::select(int p_idx, bool p_single) { } for (int i = 0; i < items.size(); i++) { - items[i].selected = p_idx == i; + items.write[i].selected = p_idx == i; } current = p_idx; @@ -258,7 +258,7 @@ void ItemList::select(int p_idx, bool p_single) { } else { if (items[p_idx].selectable && !items[p_idx].disabled) { - items[p_idx].selected = true; + items.write[p_idx].selected = true; } } update(); @@ -268,10 +268,10 @@ void ItemList::unselect(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); if (select_mode != SELECT_MULTI) { - items[p_idx].selected = false; + items.write[p_idx].selected = false; current = -1; } else { - items[p_idx].selected = false; + items.write[p_idx].selected = false; } update(); } @@ -283,7 +283,7 @@ void ItemList::unselect_all() { for (int i = 0; i < items.size(); i++) { - items[i].selected = false; + items.write[i].selected = false; } current = -1; update(); @@ -869,8 +869,8 @@ void ItemList::_notification(int p_what) { // elements need to adapt to the selected size minsize.y += vseparation; minsize.x += hseparation; - items[i].rect_cache.size = minsize; - items[i].min_rect_cache.size = minsize; + items.write[i].rect_cache.size = minsize; + items.write[i].min_rect_cache.size = minsize; } int fit_size = size.x - bg->get_minimum_size().width - mw; @@ -897,8 +897,8 @@ void ItemList::_notification(int p_what) { } if (same_column_width) - items[i].rect_cache.size.x = max_column_width; - items[i].rect_cache.position = ofs; + items.write[i].rect_cache.size.x = max_column_width; + items.write[i].rect_cache.position = ofs; max_h = MAX(max_h, items[i].rect_cache.size.y); ofs.x += items[i].rect_cache.size.x + hseparation; col++; @@ -908,7 +908,7 @@ void ItemList::_notification(int p_what) { separators.push_back(ofs.y + max_h + vseparation / 2); for (int j = i; j >= 0 && col > 0; j--, col--) { - items[j].rect_cache.size.y = max_h; + items.write[j].rect_cache.size.y = max_h; } ofs.x = 0; @@ -919,7 +919,7 @@ void ItemList::_notification(int p_what) { } for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { - items[j].rect_cache.size.y = max_h; + items.write[j].rect_cache.size.y = max_h; } if (all_fit) { @@ -1103,8 +1103,8 @@ void ItemList::_notification(int p_what) { int cs = j < ss ? font->get_char_size(items[i].text[j], items[i].text[j + 1]).x : 0; if (ofs + cs > max_len || j == ss) { - line_limit_cache[line] = j; - line_size_cache[line] = ofs; + line_limit_cache.write[line] = j; + line_size_cache.write[line] = ofs; line++; ofs = 0; if (line >= max_text_lines) diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index b71a4dd133..549daecdae 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -35,7 +35,9 @@ #include "os/os.h" #include "print_string.h" #include "translation.h" + #ifdef TOOLS_ENABLED +#include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif @@ -64,6 +66,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { _reset_caret_blink_timer(); if (b->is_pressed()) { + if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) { + clear_button_status.press_attempt = true; + clear_button_status.pressing_inside = true; + return; + } + shift_selection_check_pre(b->get_shift()); set_cursor_at_pixel_pos(b->get_position().x); @@ -100,6 +108,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } else { + if (!text.empty() && is_editable() && clear_button_enabled) { + bool press_attempt = clear_button_status.press_attempt; + clear_button_status.press_attempt = false; + if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) { + clear(); + return; + } + } + if ((!selection.creating) && (!selection.doubleclick)) { deselect(); } @@ -117,6 +134,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (m.is_valid()) { + if (!text.empty() && is_editable() && clear_button_enabled) { + bool last_press_inside = clear_button_status.pressing_inside; + clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position()); + if (last_press_inside != clear_button_status.pressing_inside) { + update(); + } + } + if (m->get_button_mask() & BUTTON_LEFT) { if (selection.creating) { @@ -548,6 +573,25 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) { } } +Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const { + if (!text.empty() && is_editable() && _is_over_clear_button(p_pos)) { + return CURSOR_ARROW; + } + return Control::get_cursor_shape(p_pos); +} + +bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const { + if (!clear_button_enabled || !has_point(p_pos)) { + return false; + } + Ref<Texture> icon = Control::get_icon("clear"); + int x_ofs = get_stylebox("normal")->get_offset().x; + if (p_pos.x > get_size().width - icon->get_width() - x_ofs) { + return true; + } + return false; +} + void LineEdit::_notification(int p_what) { switch (p_what) { @@ -640,7 +684,7 @@ void LineEdit::_notification(int p_what) { int char_ofs = window_pos; int y_area = height - style->get_minimum_size().height; - int y_ofs = style->get_offset().y; + int y_ofs = style->get_offset().y + (y_area - font->get_height()) / 2; int font_ascent = font->get_ascent(); @@ -655,10 +699,26 @@ void LineEdit::_notification(int p_what) { font_color.a *= placeholder_alpha; font_color.a *= disabled_alpha; - if (has_icon("right_icon")) { - Ref<Texture> r_icon = Control::get_icon("right_icon"); - ofs_max -= r_icon->get_width(); - r_icon->draw(ci, Point2(width - r_icon->get_width() - x_ofs, height / 2 - r_icon->get_height() / 2), Color(1, 1, 1, disabled_alpha * .9)); + bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; + if (right_icon.is_valid() || display_clear_icon) { + Ref<Texture> r_icon = display_clear_icon ? Control::get_icon("clear") : right_icon; + Color color_icon(1, 1, 1, disabled_alpha * .9); + if (display_clear_icon) { + if (clear_button_status.press_attempt && clear_button_status.pressing_inside) { + color_icon = get_color("clear_button_color_pressed"); + } else { + color_icon = get_color("clear_button_color"); + } + } + r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); + + if (align == ALIGN_CENTER) { + if (window_pos == 0) { + x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2); + } + } else { + x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT)); + } } int caret_height = font->get_height() > y_area ? y_area : font->get_height(); @@ -716,7 +776,11 @@ void LineEdit::_notification(int p_what) { if (char_ofs == cursor_pos && draw_caret) { if (ime_text.length() == 0) { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); +#else VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); +#endif } } @@ -755,7 +819,11 @@ void LineEdit::_notification(int p_what) { if (char_ofs == cursor_pos && draw_caret) { //may be at the end if (ime_text.length() == 0) { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); +#else VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); +#endif } } @@ -1086,9 +1154,8 @@ void LineEdit::set_cursor_position(int p_pos) { } else if (cursor_pos > window_pos) { /* Adjust window if cursor goes too much to the right */ int window_width = get_size().width - style->get_minimum_size().width; - if (has_icon("right_icon")) { - Ref<Texture> r_icon = Control::get_icon("right_icon"); - window_width -= r_icon->get_width(); + if (right_icon.is_valid()) { + window_width -= right_icon->get_width(); } if (window_width < 0) @@ -1375,10 +1442,26 @@ void LineEdit::set_expand_to_text_length(bool p_enabled) { } bool LineEdit::get_expand_to_text_length() const { - return expand_to_text_length; } +void LineEdit::set_clear_button_enabled(bool p_enabled) { + clear_button_enabled = p_enabled; + update(); +} + +bool LineEdit::is_clear_button_enabled() const { + return clear_button_enabled; +} + +void LineEdit::set_right_icon(const Ref<Texture> &p_icon) { + if (right_icon == p_icon) { + return; + } + right_icon = p_icon; + update(); +} + void LineEdit::_ime_text_callback(void *p_self, String p_text, Point2 p_selection) { LineEdit *self = (LineEdit *)p_self; self->ime_text = p_text; @@ -1471,6 +1554,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled); + ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled); + ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); @@ -1498,6 +1583,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); @@ -1522,6 +1608,7 @@ LineEdit::LineEdit() { secret_character = "*"; text_changed_dirty = false; placeholder_alpha = 0.6; + clear_button_enabled = false; deselect(); set_focus_mode(FOCUS_ALL); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index e9314ba8dd..5294d99da0 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -87,6 +87,10 @@ private: int cached_width; int cached_placeholder_width; + bool clear_button_enabled; + + Ref<Texture> right_icon; + struct Selection { int begin; @@ -105,6 +109,13 @@ private: List<TextOperation> undo_stack; List<TextOperation>::Element *undo_stack_pos; + struct ClearButtonStatus { + bool press_attempt; + bool pressing_inside; + } clear_button_status; + + bool _is_over_clear_button(const Point2 &p_pos) const; + void _clear_undo_stack(); void _clear_redo(); void _create_undo_state(); @@ -150,6 +161,8 @@ public: virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); + virtual CursorShape get_cursor_shape(const Point2 &p_pos) const; + void menu_option(int p_option); void set_context_menu_enabled(bool p_enable); bool is_context_menu_enabled(); @@ -201,6 +214,11 @@ public: void set_expand_to_text_length(bool p_enabled); bool get_expand_to_text_length() const; + void set_clear_button_enabled(bool p_enabled); + bool is_clear_button_enabled() const; + + void set_right_icon(const Ref<Texture> &p_icon); + virtual bool is_text_field() const; LineEdit(); ~LineEdit(); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 2356444ecb..0636accfee 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -43,7 +43,6 @@ class MenuButton : public Button { bool clicked; bool disable_shortcuts; PopupMenu *popup; - virtual void pressed(); void _unhandled_key_input(Ref<InputEvent> p_event); Array _get_items() const; @@ -55,6 +54,8 @@ protected: static void _bind_methods(); public: + virtual void pressed(); + PopupMenu *get_popup() const; void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index ebec61ee6d..ab762e19ee 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -410,7 +410,7 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < items.size(); i++) { - items[i].xl_text = tr(items[i].text); + items.write[i].xl_text = tr(items[i].text); } minimum_size_changed(); @@ -524,12 +524,17 @@ void PopupMenu::_notification(int p_what) { font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, i == mouse_over ? font_color_hover : font_color_accel); } - items[i]._ofs_cache = ofs.y; + items.write[i]._ofs_cache = ofs.y; ofs.y += h; } } break; + case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { + + if (hide_on_window_lose_focus) + hide(); + } break; case NOTIFICATION_MOUSE_ENTER: { grab_focus(); @@ -622,7 +627,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) { add_check_item(p_label, p_ID, p_accel); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -630,7 +635,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) { add_icon_check_item(p_icon, p_label, p_ID, p_accel); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -702,7 +707,7 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { add_check_shortcut(p_shortcut, p_ID, p_global); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -724,8 +729,8 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int void PopupMenu::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].text = p_text; - items[p_idx].xl_text = tr(p_text); + items.write[p_idx].text = p_text; + items.write[p_idx].xl_text = tr(p_text); update(); minimum_size_changed(); @@ -733,7 +738,7 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon = p_icon; + items.write[p_idx].icon = p_icon; update(); minimum_size_changed(); @@ -742,7 +747,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checked = p_checked; + items.write[p_idx].checked = p_checked; update(); minimum_size_changed(); @@ -750,7 +755,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { void PopupMenu::set_item_id(int p_idx, int p_ID) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].ID = p_ID; + items.write[p_idx].ID = p_ID; update(); minimum_size_changed(); @@ -759,7 +764,7 @@ void PopupMenu::set_item_id(int p_idx, int p_ID) { void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].accel = p_accel; + items.write[p_idx].accel = p_accel; update(); minimum_size_changed(); @@ -768,7 +773,7 @@ void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].metadata = p_meta; + items.write[p_idx].metadata = p_meta; update(); minimum_size_changed(); } @@ -776,7 +781,7 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].disabled = p_disabled; + items.write[p_idx].disabled = p_disabled; update(); minimum_size_changed(); } @@ -784,7 +789,7 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].submenu = p_submenu; + items.write[p_idx].submenu = p_submenu; update(); minimum_size_changed(); } @@ -792,7 +797,7 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checked = !items[p_idx].checked; + items.write[p_idx].checked = !items[p_idx].checked; update(); minimum_size_changed(); } @@ -886,7 +891,7 @@ int PopupMenu::get_item_state(int p_idx) const { void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].separator = p_separator; + items.write[p_idx].separator = p_separator; update(); } @@ -898,21 +903,21 @@ bool PopupMenu::is_item_separator(int p_idx) const { void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; update(); } void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; + items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; update(); } void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip = p_tooltip; + items.write[p_idx].tooltip = p_tooltip; update(); } @@ -921,8 +926,8 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo if (items[p_idx].shortcut.is_valid()) { _unref_shortcut(items[p_idx].shortcut); } - items[p_idx].shortcut = p_shortcut; - items[p_idx].shortcut_is_global = p_global; + items.write[p_idx].shortcut = p_shortcut; + items.write[p_idx].shortcut_is_global = p_global; if (items[p_idx].shortcut.is_valid()) { _ref_shortcut(items[p_idx].shortcut); @@ -934,7 +939,7 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo void PopupMenu::set_item_h_offset(int p_idx, int p_offset) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].h_ofs = p_offset; + items.write[p_idx].h_ofs = p_offset; update(); minimum_size_changed(); } @@ -942,14 +947,14 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) { void PopupMenu::set_item_multistate(int p_idx, int p_state) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].state = p_state; + items.write[p_idx].state = p_state; update(); } void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].shortcut_is_disabled = p_disabled; + items.write[p_idx].shortcut_is_disabled = p_disabled; update(); } @@ -960,9 +965,9 @@ void PopupMenu::toggle_item_multistate(int p_idx) { return; } - ++items[p_idx].state; - if (items[p_idx].max_states <= items[p_idx].state) - items[p_idx].state = 0; + ++items.write[p_idx].state; + if (items.write[p_idx].max_states <= items[p_idx].state) + items.write[p_idx].state = 0; update(); } @@ -1249,6 +1254,16 @@ float PopupMenu::get_submenu_popup_delay() const { return submenu_timer->get_wait_time(); } +void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) { + + hide_on_window_lose_focus = p_enabled; +} + +bool PopupMenu::is_hide_on_window_lose_focus() const { + + return hide_on_window_lose_focus; +} + String PopupMenu::get_tooltip(const Point2 &p_pos) const { int over = _get_mouse_over(p_pos); @@ -1353,6 +1368,10 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay); ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay); + + ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 8ec51c7d3a..a06a17c9fe 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -101,6 +101,7 @@ class PopupMenu : public Popup { bool hide_on_item_selection; bool hide_on_checkable_item_selection; bool hide_on_multistate_item_selection; + bool hide_on_window_lose_focus; Vector2 moved; Array _get_items() const; @@ -207,6 +208,9 @@ public: virtual void popup(const Rect2 &p_bounds = Rect2()); + void set_hide_on_window_lose_focus(bool p_enabled); + bool is_hide_on_window_lose_focus() const; + PopupMenu(); ~PopupMenu(); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 857ae8ff4c..a3748bf14c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -149,7 +149,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (r_click_item) *r_click_item = NULL; } - Line &l = p_frame->lines[p_line]; + Line &l = p_frame->lines.write[p_line]; Item *it = l.from; int line_ofs = 0; @@ -535,9 +535,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int idx = 0; //set minimums to zero for (int i = 0; i < table->columns.size(); i++) { - table->columns[i].min_width = 0; - table->columns[i].max_width = 0; - table->columns[i].width = 0; + table->columns.write[i].min_width = 0; + table->columns.write[i].max_width = 0; + table->columns.write[i].width = 0; } //compute minimum width for each cell const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; @@ -553,8 +553,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); - table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); - table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); + table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } idx++; } @@ -568,16 +568,16 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < table->columns.size(); i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) - table->columns[i].expand = true; + table->columns.write[i].expand = true; if (table->columns[i].expand) total_ratio += table->columns[i].expand_ratio; } //assign actual widths for (int i = 0; i < table->columns.size(); i++) { - table->columns[i].width = table->columns[i].min_width; + table->columns.write[i].width = table->columns[i].min_width; if (table->columns[i].expand) - table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; table->total_width += table->columns[i].width + hseparation; } @@ -592,7 +592,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int dif = table->columns[i].width - table->columns[i].max_width; if (dif > 0) { table_need_fit = true; - table->columns[i].width = table->columns[i].max_width; + table->columns.write[i].width = table->columns[i].max_width; table->total_width -= dif; total_ratio -= table->columns[i].expand_ratio; } @@ -606,7 +606,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (dif > 0) { int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; int incr = MIN(dif, slice); - table->columns[i].width += incr; + table->columns.write[i].width += incr; table->total_width += incr; } } @@ -626,8 +626,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int ly = 0; _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); - frame->lines[i].height_cache = ly; //actual height - frame->lines[i].height_accum_cache = ly; //actual height + frame->lines.write[i].height_cache = ly; //actual height + frame->lines.write[i].height_accum_cache = ly; //actual height } idx++; } @@ -669,7 +669,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & yofs += frame->lines[i].height_cache; if (p_mode == PROCESS_CACHE) { - frame->lines[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; + frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; } } @@ -1267,11 +1267,11 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int y = 0; _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); - p_frame->lines[i].height_cache = y; - p_frame->lines[i].height_accum_cache = y; + p_frame->lines.write[i].height_cache = y; + p_frame->lines.write[i].height_accum_cache = y; if (i > 0) - p_frame->lines[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache; + p_frame->lines.write[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache; } int total_height = 0; @@ -1346,7 +1346,7 @@ void RichTextLabel::add_text(const String &p_text) { _add_item(item, false); current_frame->lines.resize(current_frame->lines.size() + 1); if (item->type != ITEM_NEWLINE) - current_frame->lines[current_frame->lines.size() - 1].from = item; + current_frame->lines.write[current_frame->lines.size() - 1].from = item; _invalidate_current_line(current_frame); } @@ -1369,7 +1369,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) } if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) { - current_frame->lines[current_frame->lines.size() - 1].from = p_item; + current_frame->lines.write[current_frame->lines.size() - 1].from = p_item; } p_item->line = current_frame->lines.size() - 1; @@ -1431,7 +1431,7 @@ bool RichTextLabel::remove_line(const int p_line) { _remove_item(current->subitems[lines], current->subitems[lines]->line, lines); if (p_line == 0) { - main->lines[0].from = main; + main->lines.write[0].from = main; } main->first_invalid_line = 0; @@ -1510,8 +1510,8 @@ void RichTextLabel::push_table(int p_columns) { item->columns.resize(p_columns); item->total_width = 0; for (int i = 0; i < item->columns.size(); i++) { - item->columns[i].expand = false; - item->columns[i].expand_ratio = 1; + item->columns.write[i].expand = false; + item->columns.write[i].expand_ratio = 1; } _add_item(item, true, true); } @@ -1521,8 +1521,8 @@ void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_r ERR_FAIL_COND(current->type != ITEM_TABLE); ItemTable *table = static_cast<ItemTable *>(current); ERR_FAIL_INDEX(p_column, table->columns.size()); - table->columns[p_column].expand = p_expand; - table->columns[p_column].expand_ratio = p_ratio; + table->columns.write[p_column].expand = p_expand; + table->columns.write[p_column].expand_ratio = p_ratio; } void RichTextLabel::push_cell() { @@ -1536,7 +1536,7 @@ void RichTextLabel::push_cell() { item->cell = true; item->parent_line = item->parent_frame->lines.size() - 1; item->lines.resize(1); - item->lines[0].from = NULL; + item->lines.write[0].from = NULL; item->first_invalid_line = 0; } @@ -2269,7 +2269,7 @@ RichTextLabel::RichTextLabel() { main->index = 0; current = main; main->lines.resize(1); - main->lines[0].from = main; + main->lines.write[0].from = main; main->first_invalid_line = 0; current_frame = main; tab_size = 4; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index b114264de1..2075f7ce70 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -189,7 +189,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { update(); } - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) { // clicks Point2 pos(mb->get_position().x, mb->get_position().y); @@ -286,7 +286,7 @@ void Tabs::_notification(int p_what) { for (int i = 0; i < tabs.size(); i++) { - tabs[i].ofs_cache = mw; + tabs.write[i].ofs_cache = mw; mw += get_tab_width(i); } @@ -314,7 +314,7 @@ void Tabs::_notification(int p_what) { if (i < offset) continue; - tabs[i].ofs_cache = w; + tabs.write[i].ofs_cache = w; int lsize = tabs[i].size_cache; @@ -379,7 +379,7 @@ void Tabs::_notification(int p_what) { rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP))); w += rb->get_width(); - tabs[i].rb_rect = rb_rect; + tabs.write[i].rb_rect = rb_rect; } if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { @@ -403,7 +403,7 @@ void Tabs::_notification(int p_what) { cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP))); w += cb->get_width(); - tabs[i].cb_rect = cb_rect; + tabs.write[i].cb_rect = cb_rect; } w += sb->get_margin(MARGIN_RIGHT); @@ -471,7 +471,7 @@ bool Tabs::get_offset_buttons_visible() const { void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].text = p_title; + tabs.write[p_tab].text = p_title; update(); minimum_size_changed(); } @@ -485,7 +485,7 @@ String Tabs::get_tab_title(int p_tab) const { void Tabs::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].icon = p_icon; + tabs.write[p_tab].icon = p_icon; update(); minimum_size_changed(); } @@ -499,7 +499,7 @@ Ref<Texture> Tabs::get_tab_icon(int p_tab) const { void Tabs::set_tab_disabled(int p_tab, bool p_disabled) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].disabled = p_disabled; + tabs.write[p_tab].disabled = p_disabled; update(); } bool Tabs::get_tab_disabled(int p_tab) const { @@ -511,7 +511,7 @@ bool Tabs::get_tab_disabled(int p_tab) const { void Tabs::set_tab_right_button(int p_tab, const Ref<Texture> &p_right_button) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].right_button = p_right_button; + tabs.write[p_tab].right_button = p_right_button; _update_cache(); update(); minimum_size_changed(); @@ -536,9 +536,9 @@ void Tabs::_update_cache() { int size_fixed = 0; int count_resize = 0; for (int i = 0; i < tabs.size(); i++) { - tabs[i].ofs_cache = mw; - tabs[i].size_cache = get_tab_width(i); - tabs[i].size_text = font->get_string_size(tabs[i].text).width; + tabs.write[i].ofs_cache = mw; + tabs.write[i].size_cache = get_tab_width(i); + tabs.write[i].size_text = font->get_string_size(tabs[i].text).width; mw += tabs[i].size_cache; if (tabs[i].size_cache <= min_width || i == current) { size_fixed += tabs[i].size_cache; @@ -579,9 +579,9 @@ void Tabs::_update_cache() { lsize = m_width; } } - tabs[i].ofs_cache = w; - tabs[i].size_cache = lsize; - tabs[i].size_text = slen; + tabs.write[i].ofs_cache = w; + tabs.write[i].size_cache = lsize; + tabs.write[i].size_text = slen; w += lsize; } } @@ -920,6 +920,14 @@ int Tabs::get_tabs_rearrange_group() const { return tabs_rearrange_group; } +void Tabs::set_select_with_rmb(bool p_enabled) { + select_with_rmb = p_enabled; +} + +bool Tabs::get_select_with_rmb() const { + return select_with_rmb; +} + void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); @@ -950,6 +958,9 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &Tabs::set_select_with_rmb); + ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb); + ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab"))); @@ -988,6 +999,8 @@ Tabs::Tabs() { offset = 0; max_drawn_tab = 0; + select_with_rmb = false; + min_width = 0; scrolling_enabled = true; buttons_visible = false; diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 3b38e7f2cb..e204f4364b 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -83,6 +83,8 @@ private: int rb_hover; bool rb_pressing; + bool select_with_rmb; + int cb_hover; bool cb_pressing; CloseButtonDisplayPolicy cb_displaypolicy; @@ -150,6 +152,9 @@ public: void set_tabs_rearrange_group(int p_group_id); int get_tabs_rearrange_group() const; + void set_select_with_rmb(bool p_enabled); + bool get_select_with_rmb() const; + void ensure_tab_visible(int p_idx); void set_min_width(int p_width); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 90cb475a7b..c702bc70d0 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -127,13 +127,13 @@ void TextEdit::Text::_update_line_cache(int p_line) const { w += get_char_width(str[i], str[i + 1], w); } - text[p_line].width_cache = w; + text.write[p_line].width_cache = w; - text[p_line].wrap_amount_cache = -1; + text.write[p_line].wrap_amount_cache = -1; //update regions - text[p_line].region_info.clear(); + text.write[p_line].region_info.clear(); for (int i = 0; i < len; i++) { @@ -172,7 +172,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { ColorRegionInfo cri; cri.end = false; cri.region = j; - text[p_line].region_info[i] = cri; + text.write[p_line].region_info[i] = cri; i += lr - 1; break; @@ -200,7 +200,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { ColorRegionInfo cri; cri.end = true; cri.region = j; - text[p_line].region_info[i] = cri; + text.write[p_line].region_info[i] = cri; i += lr - 1; break; @@ -236,7 +236,7 @@ void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const { ERR_FAIL_INDEX(p_line, text.size()); - text[p_line].wrap_amount_cache = p_wrap_amount; + text.write[p_line].wrap_amount_cache = p_wrap_amount; } int TextEdit::Text::get_line_wrap_amount(int p_line) const { @@ -249,14 +249,14 @@ int TextEdit::Text::get_line_wrap_amount(int p_line) const { void TextEdit::Text::clear_width_cache() { for (int i = 0; i < text.size(); i++) { - text[i].width_cache = -1; + text.write[i].width_cache = -1; } } void TextEdit::Text::clear_wrap_cache() { for (int i = 0; i < text.size(); i++) { - text[i].wrap_amount_cache = -1; + text.write[i].wrap_amount_cache = -1; } } @@ -281,9 +281,9 @@ void TextEdit::Text::set(int p_line, const String &p_text) { ERR_FAIL_INDEX(p_line, text.size()); - text[p_line].width_cache = -1; - text[p_line].wrap_amount_cache = -1; - text[p_line].data = p_text; + text.write[p_line].width_cache = -1; + text.write[p_line].wrap_amount_cache = -1; + text.write[p_line].data = p_text; } void TextEdit::Text::insert(int p_at, const String &p_text) { @@ -337,10 +337,6 @@ void TextEdit::_update_scrollbars() { int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int first_vis_line = get_first_visible_line(); - int wi; - int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi)); - int total_rows = get_total_visible_rows(); if (scroll_past_end_of_file_enabled) { total_rows += visible_rows - 1; @@ -1152,10 +1148,18 @@ void TextEdit::_notification(int p_what) { if (ime_text.length() == 0) { if (draw_caret) { if (insert_mode) { - int caret_h = (block_caret) ? 4 : 1; +#ifdef TOOLS_ENABLED + int caret_h = (block_caret) ? 4 : 2 * EDSCALE; +#else + int caret_h = (block_caret) ? 4 : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color); } else { - caret_w = (block_caret) ? caret_w : 1; +#ifdef TOOLS_ENABLED + caret_w = (block_caret) ? caret_w : 2 * EDSCALE; +#else + caret_w = (block_caret) ? caret_w : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); } } @@ -1173,7 +1177,12 @@ void TextEdit::_notification(int p_what) { int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2; int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, yofs + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); if (underlined) { - draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color); + float line_width = 1.0; +#ifdef TOOLS_ENABLED + line_width *= EDSCALE; +#endif + + draw_rect(Rect2(char_ofs + char_margin + ofs_x, yofs + ascent + 2, w, line_width), in_selection && override_selected_font_color ? cache.font_selected_color : color); } } else if (draw_tabs && str[j] == '\t') { int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; @@ -1228,11 +1237,19 @@ void TextEdit::_notification(int p_what) { if (draw_caret) { if (insert_mode) { int char_w = cache.font->get_char_size(' ').width; - int caret_h = (block_caret) ? 4 : 1; +#ifdef TOOLS_ENABLED + int caret_h = (block_caret) ? 4 : 2 * EDSCALE; +#else + int caret_h = (block_caret) ? 4 : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color); } else { int char_w = cache.font->get_char_size(' ').width; - int caret_w = (block_caret) ? char_w : 1; +#ifdef TOOLS_ENABLED + int caret_w = (block_caret) ? char_w : 2 * EDSCALE; +#else + int caret_w = (block_caret) ? char_w : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); } } @@ -1673,7 +1690,6 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co rows /= get_row_height(); rows += get_v_scroll_offset(); int first_vis_line = get_first_visible_line(); - int last_vis_line = get_last_visible_line(); int row = first_vis_line + Math::floor(rows); int wrap_index = 0; @@ -3800,7 +3816,6 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { } // line ends before hit wrap_at; add this word to the substring wrap_substring += word_str; - px += word_px; lines.push_back(wrap_substring); return lines; } @@ -4334,7 +4349,11 @@ void TextEdit::_update_caches() { cache.search_result_border_color = get_color("search_result_border_color"); cache.symbol_color = get_color("symbol_color"); cache.background_color = get_color("background_color"); +#ifdef TOOLS_ENABLED + cache.line_spacing = get_constant("line_spacing") * EDSCALE; +#else cache.line_spacing = get_constant("line_spacing"); +#endif cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons"); @@ -5519,9 +5538,8 @@ int TextEdit::get_last_visible_line() const { int TextEdit::get_last_visible_line_wrap_index() const { int first_vis_line = get_first_visible_line(); - int last_vis_line = 0; int wi; - last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1; + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi); return wi; } @@ -5736,7 +5754,7 @@ void TextEdit::_update_completion_candidates() { for (int i = 0; i < completion_strings.size(); i++) { if (single_quote && completion_strings[i].is_quoted()) { - completion_strings[i] = completion_strings[i].unquote().quote("'"); + completion_strings.write[i] = completion_strings[i].unquote().quote("'"); } if (s == completion_strings[i]) { @@ -6397,7 +6415,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int } // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f') && !in_word && prev_is_number && !is_number) { is_number = true; is_symbol = false; is_char = false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 34d69bb508..19b5d574c6 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -101,13 +101,13 @@ public: int get_line_wrap_amount(int p_line) const; const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const; void set(int p_line, const String &p_text); - void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } + void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; } bool is_marked(int p_line) const { return text[p_line].marked; } - void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } + void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; } bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } - void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } + void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; } bool is_hidden(int p_line) const { return text[p_line].hidden; } - void set_safe(int p_line, bool p_safe) { text[p_line].safe = p_safe; } + void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; } bool is_safe(int p_line) const { return text[p_line].safe; } void insert(int p_at, const String &p_text); void remove(int p_at); diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 82d983184b..6e4fe88dbf 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -309,15 +309,23 @@ void TextureProgress::_notification(int p_what) { draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_CLOCKWISE: - case FILL_COUNTER_CLOCKWISE: { + case FILL_COUNTER_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); draw_texture_rect_region(progress, region, region, tint_progress); } else if (val != 0) { Array pts; - float direction = mode == FILL_CLOCKWISE ? 1 : -1; - float start = rad_init_angle / 360; + float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; + float start; + + if (mode == FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE) { + start = rad_init_angle / 360 - val / 2; + } else { + start = rad_init_angle / 360; + } + float end = start + direction * val; pts.append(start); pts.append(end); @@ -351,6 +359,14 @@ void TextureProgress::_notification(int p_what) { draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2); } } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; default: draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } @@ -364,7 +380,7 @@ void TextureProgress::_notification(int p_what) { } void TextureProgress::set_fill_mode(int p_fill) { - ERR_FAIL_INDEX(p_fill, 6); + ERR_FAIL_INDEX(p_fill, 9); mode = (FillMode)p_fill; update(); } @@ -446,7 +462,7 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over"); @@ -468,6 +484,9 @@ void TextureProgress::_bind_methods() { BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP); BIND_ENUM_CONSTANT(FILL_CLOCKWISE); BIND_ENUM_CONSTANT(FILL_COUNTER_CLOCKWISE); + BIND_ENUM_CONSTANT(FILL_BILINEAR_LEFT_AND_RIGHT); + BIND_ENUM_CONSTANT(FILL_BILINEAR_TOP_AND_BOTTOM); + BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE); } TextureProgress::TextureProgress() { diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index 34158b5db5..a11e55234a 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -52,7 +52,10 @@ public: FILL_TOP_TO_BOTTOM, FILL_BOTTOM_TO_TOP, FILL_CLOCKWISE, - FILL_COUNTER_CLOCKWISE + FILL_COUNTER_CLOCKWISE, + FILL_BILINEAR_LEFT_AND_RIGHT, + FILL_BILINEAR_TOP_AND_BOTTOM, + FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE }; void set_fill_mode(int p_fill); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 1d27612766..6f09488b64 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -121,7 +121,7 @@ void TreeItem::_cell_deselected(int p_cell) { void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); - Cell &c = cells[p_column]; + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; c.max = 100; @@ -144,7 +144,7 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].checked = p_checked; + cells.write[p_column].checked = p_checked; _changed_notify(p_column); } @@ -157,22 +157,22 @@ bool TreeItem::is_checked(int p_column) const { void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].text = p_text; + cells.write[p_column].text = p_text; if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE || cells[p_column].mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) { Vector<String> strings = p_text.split(","); - cells[p_column].min = INT_MAX; - cells[p_column].max = INT_MIN; + cells.write[p_column].min = INT_MAX; + cells.write[p_column].max = INT_MIN; for (int i = 0; i < strings.size(); i++) { int value = i; if (!strings[i].get_slicec(':', 1).empty()) { value = strings[i].get_slicec(':', 1).to_int(); } - cells[p_column].min = MIN(cells[p_column].min, value); - cells[p_column].max = MAX(cells[p_column].max, value); + cells.write[p_column].min = MIN(cells[p_column].min, value); + cells.write[p_column].max = MAX(cells[p_column].max, value); } - cells[p_column].step = 0; + cells.write[p_column].step = 0; } _changed_notify(p_column); } @@ -186,7 +186,7 @@ String TreeItem::get_text(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].suffix = p_suffix; + cells.write[p_column].suffix = p_suffix; _changed_notify(p_column); } @@ -200,7 +200,7 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon = p_icon; + cells.write[p_column].icon = p_icon; _changed_notify(p_column); } @@ -213,7 +213,7 @@ Ref<Texture> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_region = p_icon_region; + cells.write[p_column].icon_region = p_icon_region; _changed_notify(p_column); } @@ -226,7 +226,7 @@ Rect2 TreeItem::get_icon_region(int p_column) const { void TreeItem::set_icon_color(int p_column, const Color &p_icon_color) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_color = p_icon_color; + cells.write[p_column].icon_color = p_icon_color; _changed_notify(p_column); } @@ -239,7 +239,7 @@ Color TreeItem::get_icon_color(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_max_w = p_max; + cells.write[p_column].icon_max_w = p_max; _changed_notify(p_column); } @@ -260,7 +260,7 @@ void TreeItem::set_range(int p_column, double p_value) { if (p_value > cells[p_column].max) p_value = cells[p_column].max; - cells[p_column].val = p_value; + cells.write[p_column].val = p_value; _changed_notify(p_column); } @@ -278,10 +278,10 @@ bool TreeItem::is_range_exponential(int p_column) const { void TreeItem::set_range_config(int p_column, double p_min, double p_max, double p_step, bool p_exp) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].min = p_min; - cells[p_column].max = p_max; - cells[p_column].step = p_step; - cells[p_column].expr = p_exp; + cells.write[p_column].min = p_min; + cells.write[p_column].max = p_max; + cells.write[p_column].step = p_step; + cells.write[p_column].expr = p_exp; _changed_notify(p_column); } @@ -296,7 +296,7 @@ void TreeItem::get_range_config(int p_column, double &r_min, double &r_max, doub void TreeItem::set_metadata(int p_column, const Variant &p_meta) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].meta = p_meta; + cells.write[p_column].meta = p_meta; } Variant TreeItem::get_metadata(int p_column) const { @@ -311,8 +311,8 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_NULL(p_object); - cells[p_column].custom_draw_obj = p_object->get_instance_id(); - cells[p_column].custom_draw_callback = p_callback; + cells.write[p_column].custom_draw_obj = p_object->get_instance_id(); + cells.write[p_column].custom_draw_callback = p_callback; } void TreeItem::set_collapsed(bool p_collapsed) { @@ -467,7 +467,7 @@ void TreeItem::remove_child(TreeItem *p_item) { void TreeItem::set_selectable(int p_column, bool p_selectable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].selectable = p_selectable; + cells.write[p_column].selectable = p_selectable; } bool TreeItem::is_selectable(int p_column) const { @@ -517,7 +517,7 @@ void TreeItem::add_button(int p_column, const Ref<Texture> &p_button, int p_id, button.id = p_id; button.disabled = p_disabled; button.tooltip = p_tooltip; - cells[p_column].buttons.push_back(button); + cells.write[p_column].buttons.push_back(button); _changed_notify(p_column); } @@ -540,7 +540,7 @@ void TreeItem::erase_button(int p_column, int p_idx) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons.remove(p_idx); + cells.write[p_column].buttons.remove(p_idx); _changed_notify(p_column); } @@ -568,7 +568,7 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture> &p_button) ERR_FAIL_COND(p_button.is_null()); ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons[p_idx].texture = p_button; + cells.write[p_column].buttons.write[p_idx].texture = p_button; _changed_notify(p_column); } @@ -576,14 +576,14 @@ void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons[p_idx].color = p_color; + cells.write[p_column].buttons.write[p_idx].color = p_color; _changed_notify(p_column); } void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].editable = p_editable; + cells.write[p_column].editable = p_editable; _changed_notify(p_column); } @@ -596,8 +596,8 @@ bool TreeItem::is_editable(int p_column) { void TreeItem::set_custom_color(int p_column, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_color = true; - cells[p_column].color = p_color; + cells.write[p_column].custom_color = true; + cells.write[p_column].color = p_color; _changed_notify(p_column); } Color TreeItem::get_custom_color(int p_column) const { @@ -610,15 +610,15 @@ Color TreeItem::get_custom_color(int p_column) const { void TreeItem::clear_custom_color(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_color = false; - cells[p_column].color = Color(); + cells.write[p_column].custom_color = false; + cells.write[p_column].color = Color(); _changed_notify(p_column); } void TreeItem::set_tooltip(int p_column, const String &p_tooltip) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].tooltip = p_tooltip; + cells.write[p_column].tooltip = p_tooltip; } String TreeItem::get_tooltip(int p_column) const { @@ -630,17 +630,17 @@ String TreeItem::get_tooltip(int p_column) const { void TreeItem::set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_bg_color = true; - cells[p_column].custom_bg_outline = p_bg_outline; - cells[p_column].bg_color = p_color; + cells.write[p_column].custom_bg_color = true; + cells.write[p_column].custom_bg_outline = p_bg_outline; + cells.write[p_column].bg_color = p_color; _changed_notify(p_column); } void TreeItem::clear_custom_bg_color(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_bg_color = false; - cells[p_column].bg_color = Color(); + cells.write[p_column].custom_bg_color = false; + cells.write[p_column].bg_color = Color(); _changed_notify(p_column); } @@ -655,7 +655,7 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_button = p_button; + cells.write[p_column].custom_button = p_button; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -666,7 +666,7 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_align(int p_column, TextAlign p_align) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].text_align = p_align; + cells.write[p_column].text_align = p_align; _changed_notify(p_column); } @@ -678,7 +678,7 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].expand_right = p_enable; + cells.write[p_column].expand_right = p_enable; _changed_notify(p_column); } @@ -1486,7 +1486,7 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev, bool *r_in_range, bool p_force_deselect) { - TreeItem::Cell &selected_cell = p_selected->cells[p_col]; + TreeItem::Cell &selected_cell = p_selected->cells.write[p_col]; bool switched = false; if (r_in_range && !*r_in_range && (p_current == p_selected || p_current == p_prev)) { @@ -1498,7 +1498,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c for (int i = 0; i < columns.size(); i++) { - TreeItem::Cell &c = p_current->cells[i]; + TreeItem::Cell &c = p_current->cells.write[i]; if (!c.selectable) continue; @@ -1689,7 +1689,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool return -1; //collapse/uncollapse because nothing can be done with item } - TreeItem::Cell &c = p_item->cells[col]; + const TreeItem::Cell &c = p_item->cells[col]; bool already_selected = c.selected; bool already_cursor = (p_item == selected_item) && col == selected_col; @@ -1990,7 +1990,7 @@ void Tree::text_editor_enter(String p_text) { if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) return; - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; switch (c.mode) { case TreeItem::CELL_MODE_STRING: { @@ -2041,7 +2041,7 @@ void Tree::value_editor_changed(double p_value) { return; } - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; c.val = p_value; item_edited(popup_edited_item_col, popup_edited_item); update(); @@ -2055,7 +2055,7 @@ void Tree::popup_select(int p_option) { if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) return; - popup_edited_item->cells[popup_edited_item_col].val = p_option; + popup_edited_item->cells.write[popup_edited_item_col].val = p_option; //popup_edited_item->edited_signal.call( popup_edited_item_col ); update(); item_edited(popup_edited_item_col, popup_edited_item); @@ -2426,14 +2426,23 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { int col, h, section; TreeItem *it = _find_item_at_pos(root, mpos, col, h, section); - if ((drop_mode_flags && it != drop_mode_over) || section != drop_mode_section) { - drop_mode_over = it; - drop_mode_section = section; - update(); + if (drop_mode_flags) { + if (it != drop_mode_over) { + drop_mode_over = it; + update(); + } + if (it && section != drop_mode_section) { + drop_mode_section = section; + update(); + } } - if (it != cache.hover_item || col != cache.hover_cell) { + if (it != cache.hover_item) { cache.hover_item = it; + update(); + } + + if (it && col != cache.hover_cell) { cache.hover_cell = col; update(); } @@ -2458,7 +2467,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } else { - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + const TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; float diff_y = -mm->get_relative().y; diff_y = Math::pow(ABS(diff_y), 1.8f) * SGN(diff_y); diff_y *= 0.1; @@ -2672,7 +2681,7 @@ bool Tree::edit_selected() { popup_edited_item = s; popup_edited_item_col = col; - TreeItem::Cell &c = s->cells[col]; + const TreeItem::Cell &c = s->cells[col]; if (c.mode == TreeItem::CELL_MODE_CHECK) { @@ -3063,7 +3072,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { if (!p_item->cells[p_column].selectable) return; - p_item->cells[p_column].selected = true; + p_item->cells.write[p_column].selected = true; //emit_signal("multi_selected",p_item,p_column,true); - NO this is for TreeItem::select selected_col = p_column; @@ -3077,7 +3086,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { void Tree::item_deselected(int p_column, TreeItem *p_item) { if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) { - p_item->cells[p_column].selected = false; + p_item->cells.write[p_column].selected = false; } update(); } @@ -3158,14 +3167,14 @@ void Tree::set_column_min_width(int p_column, int p_min_width) { if (p_min_width < 1) return; - columns[p_column].min_width = p_min_width; + columns.write[p_column].min_width = p_min_width; update(); } void Tree::set_column_expand(int p_column, bool p_expand) { ERR_FAIL_INDEX(p_column, columns.size()); - columns[p_column].expand = p_expand; + columns.write[p_column].expand = p_expand; update(); } @@ -3413,7 +3422,7 @@ bool Tree::are_column_titles_visible() const { void Tree::set_column_title(int p_column, const String &p_title) { ERR_FAIL_INDEX(p_column, columns.size()); - columns[p_column].title = p_title; + columns.write[p_column].title = p_title; update(); } @@ -3659,7 +3668,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const { if (it) { - TreeItem::Cell &c = it->cells[col]; + const TreeItem::Cell &c = it->cells[col]; int col_width = get_column_width(col); for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture> b = c.buttons[j].texture; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index e1333bcae2..6144240328 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() { void Node::_propagate_exit_tree() { - //block while removing children +//block while removing children #ifdef DEBUG_ENABLED @@ -725,6 +725,17 @@ const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rset_mod return data.rpc_properties.find(p_property); } +bool Node::can_process_notification(int p_what) const { + switch (p_what) { + case NOTIFICATION_PHYSICS_PROCESS: return data.physics_process; + case NOTIFICATION_PROCESS: return data.idle_process; + case NOTIFICATION_INTERNAL_PROCESS: return data.idle_process_internal; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: return data.physics_process_internal; + } + + return true; +} + bool Node::can_process() const { ERR_FAIL_COND_V(!is_inside_tree(), false); @@ -809,6 +820,22 @@ bool Node::is_processing_internal() const { return data.idle_process_internal; } +void Node::set_process_priority(int p_priority) { + data.process_priority = p_priority; + + if (is_processing()) + data.tree->make_group_changed("idle_process"); + + if (is_processing_internal()) + data.tree->make_group_changed("idle_process_internal"); + + if (is_physics_processing()) + data.tree->make_group_changed("physics_process"); + + if (is_physics_processing_internal()) + data.tree->make_group_changed("physics_process_internal"); +} + void Node::set_process_input(bool p_enable) { if (p_enable == data.input) @@ -2608,6 +2635,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_physics_processing"), &Node::is_physics_processing); ClassDB::bind_method(D_METHOD("get_process_delta_time"), &Node::get_process_delta_time); ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process); + ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority); ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing); ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input); ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input); @@ -2759,6 +2787,7 @@ Node::Node() { data.tree = NULL; data.physics_process = false; data.idle_process = false; + data.process_priority = 0; data.physics_process_internal = false; data.idle_process_internal = false; data.inside_tree = false; diff --git a/scene/main/node.h b/scene/main/node.h index 341349de79..f3422618ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -70,6 +70,11 @@ public: bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); } }; + struct ComparatorWithPriority { + + bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; } + }; + private: struct GroupData { @@ -118,6 +123,7 @@ private: //should move all the stuff below to bits bool physics_process; bool idle_process; + int process_priority; bool physics_process_internal; bool idle_process_internal; @@ -319,6 +325,8 @@ public: void set_process_internal(bool p_idle_process_internal); bool is_processing_internal() const; + void set_process_priority(int p_priority); + void set_process_input(bool p_enable); bool is_processing_input() const; @@ -355,6 +363,7 @@ public: void set_pause_mode(PauseMode p_mode); PauseMode get_pause_mode() const; bool can_process() const; + bool can_process_notification(int p_what) const; void request_ready(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 6438616cf2..e99f785848 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -132,6 +132,12 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { group_map.erase(E); } +void SceneTree::make_group_changed(const StringName &p_group) { + Map<StringName, Group>::Element *E = group_map.find(p_group); + if (E) + E->get().changed = true; +} + void SceneTree::flush_transform_notifications() { SelfList<Node> *n = xform_change_list.first(); @@ -165,18 +171,23 @@ void SceneTree::_flush_ugc() { ugc_locked = false; } -void SceneTree::_update_group_order(Group &g) { +void SceneTree::_update_group_order(Group &g, bool p_use_priority) { if (!g.changed) return; if (g.nodes.empty()) return; - Node **nodes = &g.nodes[0]; + Node **nodes = g.nodes.ptrw(); int node_count = g.nodes.size(); - SortArray<Node *, Node::Comparator> node_sort; - node_sort.sort(nodes, node_count); + if (p_use_priority) { + SortArray<Node *, Node::ComparatorWithPriority> node_sort; + node_sort.sort(nodes, node_count); + } else { + SortArray<Node *, Node::Comparator> node_sort; + node_sort.sort(nodes, node_count); + } g.changed = false; } @@ -216,7 +227,7 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -271,7 +282,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -320,7 +331,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -884,7 +895,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p Vector<Node *> nodes_copy = g.nodes; int node_count = nodes_copy.size(); - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); Variant arg = p_input; const Variant *v[1] = { &arg }; @@ -921,14 +932,14 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (g.nodes.empty()) return; - _update_group_order(g); + _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); //copy, so copy on write happens in case something is removed from process while being called //performance is not lost because only if something is added/removed the vector is copied. Vector<Node *> nodes_copy = g.nodes; int node_count = nodes_copy.size(); - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); call_lock++; @@ -940,6 +951,8 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (!n->can_process()) continue; + if (!n->can_process_notification(p_notification)) + continue; n->notification(p_notification); //ERR_FAIL_COND(node_count != g.nodes.size()); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index aa8d78b1e1..11201097d4 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -161,7 +161,7 @@ private: bool ugc_locked; void _flush_ugc(); - _FORCE_INLINE_ void _update_group_order(Group &g); + _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); void _update_listener(); Array _get_nodes_in_group(const StringName &p_group); @@ -204,6 +204,7 @@ private: Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); + void make_group_changed(const StringName &p_group); void _notify_group_pause(const StringName &p_group, int p_notification); void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9013d276c7..e43c2da02d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -41,7 +41,10 @@ #include "scene/3d/spatial.h" #include "scene/gui/control.h" #include "scene/gui/label.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" #include "scene/gui/panel_container.h" +#include "scene/gui/popup_menu.h" #include "scene/main/timer.h" #include "scene/resources/mesh.h" #include "scene/scene_string_names.h" @@ -1853,8 +1856,32 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() == Variant::NIL && over && !gui.modal_stack.empty()) { Control *top = gui.modal_stack.back()->get(); + if (over != top && !top->is_a_parent_of(over)) { - over = NULL; //nothing can be found outside the modal stack + + PopupMenu *popup_menu = Object::cast_to<PopupMenu>(top); + MenuButton *popup_menu_parent = NULL; + MenuButton *menu_button = Object::cast_to<MenuButton>(over); + + if (popup_menu) + popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); + + // If the mouse is over a menu button, this menu will open automatically + // if there is already a pop-up menu open at the same hierarchical level. + if (popup_menu_parent && menu_button && + popup_menu_parent->get_icon().is_null() && + menu_button->get_icon().is_null() && + (popup_menu->get_parent()->get_parent()->is_a_parent_of(menu_button) || + menu_button->get_parent()->is_a_parent_of(popup_menu))) { + + popup_menu->notification(Control::NOTIFICATION_MODAL_CLOSE); + popup_menu->_modal_stack_remove(); + popup_menu->hide(); + + menu_button->pressed(); + } else { + over = NULL; //nothing can be found outside the modal stack + } } } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 1f1a7a8027..382bddfb4e 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -156,6 +156,7 @@ #include "scene/resources/sky_box.h" #include "scene/resources/sphere_shape.h" #include "scene/resources/surface_tool.h" +#include "scene/resources/text_file.h" #include "scene/resources/texture.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" @@ -199,10 +200,13 @@ #include "scene/3d/remote_transform.h" #include "scene/3d/room_instance.h" #include "scene/3d/skeleton.h" +#include "scene/3d/soft_body.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" +#include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" +#include "scene/resources/physics_material.h" #endif static ResourceFormatLoaderTheme *resource_loader_theme = NULL; @@ -213,6 +217,7 @@ static ResourceFormatLoaderText *resource_loader_text = NULL; static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font = NULL; static ResourceFormatLoaderStreamTexture *resource_loader_stream_texture = NULL; +static ResourceFormatLoaderTextureLayered *resource_loader_texture_layered = NULL; static ResourceFormatLoaderBMFont *resource_loader_bmfont = NULL; @@ -233,6 +238,9 @@ void register_scene_types() { resource_loader_stream_texture = memnew(ResourceFormatLoaderStreamTexture); ResourceLoader::add_resource_format_loader(resource_loader_stream_texture); + resource_loader_texture_layered = memnew(ResourceFormatLoaderTextureLayered); + ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); + resource_loader_theme = memnew(ResourceFormatLoaderTheme); ResourceLoader::add_resource_format_loader(resource_loader_theme); @@ -358,6 +366,7 @@ void register_scene_types() { ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); + ClassDB::register_class<SkeletonIK>(); ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); @@ -427,6 +436,7 @@ void register_scene_types() { ClassDB::register_class<KinematicCollision>(); ClassDB::register_class<KinematicBody>(); ClassDB::register_class<PhysicalBone>(); + ClassDB::register_class<SoftBody>(); ClassDB::register_class<VehicleBody>(); ClassDB::register_class<VehicleWheel>(); @@ -590,6 +600,8 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init ClassDB::register_class<SpatialVelocityTracker>(); + + ClassDB::register_class<PhysicsMaterial>(); #endif ClassDB::register_class<World>(); ClassDB::register_class<Environment>(); @@ -607,11 +619,16 @@ void register_scene_types() { ClassDB::register_class<ProxyTexture>(); ClassDB::register_class<AnimatedTexture>(); ClassDB::register_class<CubeMap>(); + ClassDB::register_virtual_class<TextureLayered>(); + ClassDB::register_class<Texture3D>(); + ClassDB::register_class<TextureArray>(); ClassDB::register_class<Animation>(); ClassDB::register_virtual_class<Font>(); ClassDB::register_class<BitmapFont>(); ClassDB::register_class<Curve>(); + ClassDB::register_class<TextFile>(); + ClassDB::register_class<DynamicFontData>(); ClassDB::register_class<DynamicFont>(); @@ -718,6 +735,7 @@ void unregister_scene_types() { memdelete(resource_loader_dynamic_font); memdelete(resource_loader_stream_texture); + memdelete(resource_loader_texture_layered); memdelete(resource_loader_theme); DynamicFont::finish_dynamic_fonts(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 8acfdc482a..7041b62487 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -100,7 +100,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < (vcount / 12); i++) { - TKey<TransformKey> &tk = tt->transforms[i]; + TKey<TransformKey> &tk = tt->transforms.write[i]; const float *ofs = &r[i * 12]; tk.time = ofs[0]; tk.transition = ofs[1]; @@ -154,8 +154,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - vt->values[i].time = rt[i]; - vt->values[i].value = values[i]; + vt->values.write[i].time = rt[i]; + vt->values.write[i].value = values[i]; } if (d.has("transitions")) { @@ -167,7 +167,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - vt->values[i].transition = rtr[i]; + vt->values.write[i].transition = rtr[i]; } } } @@ -235,13 +235,13 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - bt->values[i].time = rt[i]; - bt->values[i].transition = 0; //unused in bezier - bt->values[i].value.value = rv[i * 5 + 0]; - bt->values[i].value.in_handle.x = rv[i * 5 + 1]; - bt->values[i].value.in_handle.y = rv[i * 5 + 2]; - bt->values[i].value.out_handle.x = rv[i * 5 + 3]; - bt->values[i].value.out_handle.y = rv[i * 5 + 4]; + bt->values.write[i].time = rt[i]; + bt->values.write[i].transition = 0; //unused in bezier + bt->values.write[i].value.value = rv[i * 5 + 0]; + bt->values.write[i].value.in_handle.x = rv[i * 5 + 1]; + bt->values.write[i].value.in_handle.y = rv[i * 5 + 2]; + bt->values.write[i].value.out_handle.x = rv[i * 5 + 3]; + bt->values.write[i].value.out_handle.y = rv[i * 5 + 4]; } } @@ -313,7 +313,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { TKey<StringName> ak; ak.time = rt[i]; ak.value = rc[i]; - an->values[i] = ak; + an->values.write[i] = ak; } } @@ -822,7 +822,7 @@ int Animation::_insert(float p_time, T &p_keys, const V &p_value) { } else if (p_keys[idx - 1].time == p_time) { // condition for replacing. - p_keys[idx - 1] = p_value; + p_keys.write[idx - 1] = p_value; return idx - 1; } @@ -1349,18 +1349,18 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); Dictionary d = p_value; if (d.has("location")) - tt->transforms[p_key_idx].value.loc = d["location"]; + tt->transforms.write[p_key_idx].value.loc = d["location"]; if (d.has("rotation")) - tt->transforms[p_key_idx].value.rot = d["rotation"]; + tt->transforms.write[p_key_idx].value.rot = d["rotation"]; if (d.has("scale")) - tt->transforms[p_key_idx].value.scale = d["scale"]; + tt->transforms.write[p_key_idx].value.scale = d["scale"]; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); - vt->values[p_key_idx].value = p_value; + vt->values.write[p_key_idx].value = p_value; } break; case TYPE_METHOD: { @@ -1369,9 +1369,9 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, mt->methods.size()); Dictionary d = p_value; if (d.has("method")) - mt->methods[p_key_idx].method = d["method"]; + mt->methods.write[p_key_idx].method = d["method"]; if (d.has("args")) - mt->methods[p_key_idx].params = d["args"]; + mt->methods.write[p_key_idx].params = d["args"]; } break; case TYPE_BEZIER: { @@ -1381,11 +1381,11 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Array arr = p_value; ERR_FAIL_COND(arr.size() != 5); - bt->values[p_key_idx].value.value = arr[0]; - bt->values[p_key_idx].value.in_handle.x = arr[1]; - bt->values[p_key_idx].value.in_handle.y = arr[2]; - bt->values[p_key_idx].value.out_handle.x = arr[3]; - bt->values[p_key_idx].value.out_handle.y = arr[4]; + bt->values.write[p_key_idx].value.value = arr[0]; + bt->values.write[p_key_idx].value.in_handle.x = arr[1]; + bt->values.write[p_key_idx].value.in_handle.y = arr[2]; + bt->values.write[p_key_idx].value.out_handle.x = arr[3]; + bt->values.write[p_key_idx].value.out_handle.y = arr[4]; } break; case TYPE_AUDIO: { @@ -1397,16 +1397,16 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_COND(!k.has("end_offset")); ERR_FAIL_COND(!k.has("stream")); - at->values[p_key_idx].value.start_offset = k["start_offset"]; - at->values[p_key_idx].value.end_offset = k["end_offset"]; - at->values[p_key_idx].value.stream = k["stream"]; + at->values.write[p_key_idx].value.start_offset = k["start_offset"]; + at->values.write[p_key_idx].value.end_offset = k["end_offset"]; + at->values.write[p_key_idx].value.stream = k["stream"]; } break; case TYPE_ANIMATION: { AnimationTrack *at = static_cast<AnimationTrack *>(t); - at->values[p_key_idx].value = p_value; + at->values.write[p_key_idx].value = p_value; } break; } @@ -1423,20 +1423,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - tt->transforms[p_key_idx].transition = p_transition; + tt->transforms.write[p_key_idx].transition = p_transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); - vt->values[p_key_idx].transition = p_transition; + vt->values.write[p_key_idx].transition = p_transition; } break; case TYPE_METHOD: { MethodTrack *mt = static_cast<MethodTrack *>(t); ERR_FAIL_INDEX(p_key_idx, mt->methods.size()); - mt->methods[p_key_idx].transition = p_transition; + mt->methods.write[p_key_idx].transition = p_transition; } break; case TYPE_BEZIER: @@ -2210,7 +2210,7 @@ void Animation::bezier_track_set_key_value(int p_track, int p_index, float p_val ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.value = p_value; + bt->values.write[p_index].value.value = p_value; emit_changed(); } @@ -2224,9 +2224,9 @@ void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const V ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.in_handle = p_handle; + bt->values.write[p_index].value.in_handle = p_handle; if (bt->values[p_index].value.in_handle.x > 0) { - bt->values[p_index].value.in_handle.x = 0; + bt->values.write[p_index].value.in_handle.x = 0; } emit_changed(); } @@ -2240,9 +2240,9 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.out_handle = p_handle; + bt->values.write[p_index].value.out_handle = p_handle; if (bt->values[p_index].value.out_handle.x < 0) { - bt->values[p_index].value.out_handle.x = 0; + bt->values.write[p_index].value.out_handle.x = 0; } emit_changed(); } @@ -2396,7 +2396,7 @@ void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_ ERR_FAIL_INDEX(p_key, at->values.size()); - at->values[p_key].value.stream = p_stream; + at->values.write[p_key].value.stream = p_stream; emit_changed(); } @@ -2414,7 +2414,7 @@ void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p if (p_offset < 0) p_offset = 0; - at->values[p_key].value.start_offset = p_offset; + at->values.write[p_key].value.start_offset = p_offset; emit_changed(); } @@ -2432,7 +2432,7 @@ void Animation::audio_track_set_key_end_offset(int p_track, int p_key, float p_o if (p_offset < 0) p_offset = 0; - at->values[p_key].value.end_offset = p_offset; + at->values.write[p_key].value.end_offset = p_offset; emit_changed(); } @@ -2505,7 +2505,7 @@ void Animation::animation_track_set_key_animation(int p_track, int p_key, const ERR_FAIL_INDEX(p_key, at->values.size()); - at->values[p_key].value = p_animation; + at->values.write[p_key].value = p_animation; emit_changed(); } @@ -2550,7 +2550,7 @@ void Animation::track_move_up(int p_track) { if (p_track >= 0 && p_track < (tracks.size() - 1)) { - SWAP(tracks[p_track], tracks[p_track + 1]); + SWAP(tracks.write[p_track], tracks.write[p_track + 1]); } emit_changed(); @@ -2585,7 +2585,7 @@ void Animation::track_move_down(int p_track) { if (p_track > 0 && p_track < tracks.size()) { - SWAP(tracks[p_track], tracks[p_track - 1]); + SWAP(tracks.write[p_track], tracks.write[p_track - 1]); } emit_changed(); } @@ -2596,7 +2596,7 @@ void Animation::track_swap(int p_track, int p_with_track) { ERR_FAIL_INDEX(p_with_track, tracks.size()); if (p_track == p_with_track) return; - SWAP(tracks[p_track], tracks[p_with_track]); + SWAP(tracks.write[p_track], tracks.write[p_with_track]); emit_changed(); } @@ -2927,9 +2927,9 @@ void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err, for (int i = 1; i < tt->transforms.size() - 1; i++) { - TKey<TransformKey> &t0 = tt->transforms[i - 1]; - TKey<TransformKey> &t1 = tt->transforms[i]; - TKey<TransformKey> &t2 = tt->transforms[i + 1]; + TKey<TransformKey> &t0 = tt->transforms.write[i - 1]; + TKey<TransformKey> &t1 = tt->transforms.write[i]; + TKey<TransformKey> &t2 = tt->transforms.write[i + 1]; bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm); if (erase && !prev_erased) { diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 02a9e4d69b..e6a4b01deb 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "audio_stream_sample.h" +#include "io/marshalls.h" +#include "os/file_access.h" void AudioStreamPlaybackSample::start(float p_from_pos) { @@ -509,6 +511,76 @@ PoolVector<uint8_t> AudioStreamSample::get_data() const { return pv; } +void AudioStreamSample::save_to_wav(String p_path) { + if (format == AudioStreamSample::FORMAT_IMA_ADPCM) { + WARN_PRINTS("Saving IMA_ADPC samples are not supported yet"); + return; + } + + int sub_chunk_2_size = data_bytes; //Subchunk2Size = Size of data in bytes + + // Format code + // 1:PCM format (for 8 or 16 bit) + // 3:IEEE float format + int format_code = (format == FORMAT_IMA_ADPCM) ? 3 : 1; + + int n_channels = stereo ? 2 : 1; + + long sample_rate = mix_rate; + + int byte_pr_sample = 0; + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: byte_pr_sample = 1; break; + case AudioStreamSample::FORMAT_16_BITS: byte_pr_sample = 2; break; + case AudioStreamSample::FORMAT_IMA_ADPCM: byte_pr_sample = 4; break; + } + + String file_path = p_path; + if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) { + file_path += ".wav"; + } + + Error err; + FileAccess *file = FileAccess::open(file_path, FileAccess::WRITE, &err); //Overrides existing file if present + + // Create WAV Header + file->store_string("RIFF"); //ChunkID + file->store_32(sub_chunk_2_size + 36); //ChunkSize = 36 + SubChunk2Size (size of entire file minus the 8 bits for this and previous header) + file->store_string("WAVE"); //Format + file->store_string("fmt "); //Subchunk1ID + file->store_32(16); //Subchunk1Size = 16 + file->store_16(format_code); //AudioFormat + file->store_16(n_channels); //Number of Channels + file->store_32(sample_rate); //SampleRate + file->store_32(sample_rate * n_channels * byte_pr_sample); //ByteRate + file->store_16(n_channels * byte_pr_sample); //BlockAlign = NumChannels * BytePrSample + file->store_16(byte_pr_sample * 8); //BitsPerSample + file->store_string("data"); //Subchunk2ID + file->store_32(sub_chunk_2_size); //Subchunk2Size + + // Add data + PoolVector<uint8_t>::Read read_data = get_data().read(); + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: + for (int i = 0; i < data_bytes; i++) { + uint8_t data_point = (read_data[i] + 128); + file->store_8(data_point); + } + break; + case AudioStreamSample::FORMAT_16_BITS: + for (int i = 0; i < data_bytes / 2; i++) { + uint16_t data_point = decode_uint16(&read_data[i * 2]); + file->store_16(data_point); + } + break; + case AudioStreamSample::FORMAT_IMA_ADPCM: + //Unimplemented + break; + } + + file->close(); +} + Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { Ref<AudioStreamPlaybackSample> sample; @@ -545,6 +617,8 @@ void AudioStreamSample::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo); ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo); + ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav); + ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong"), "set_loop_mode", "get_loop_mode"); diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index 5fe65c194e..a27acc92b7 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -140,6 +140,8 @@ public: void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; + void save_to_wav(String p_path); + virtual Ref<AudioStreamPlayback> instance_playback(); virtual String get_stream_name() const; diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp index 29ffefd9d6..ec1aa7ec46 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -130,7 +130,7 @@ void BitMap::set_bit(const Point2 &p_pos, bool p_value) { else b &= ~(1 << bbit); - bitmask[bbyte] = b; + bitmask.write[bbyte] = b; } bool BitMap::get_bit(const Point2 &p_pos) const { @@ -322,8 +322,8 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points[_points.size() - 1].x = (float)(curx - rect.position.x); - _points[_points.size() - 1].y = (float)(cury + rect.position.y); + _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); + _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); } else { _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); } @@ -373,11 +373,11 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { Vector<Vector2> left, right; left.resize(index); for (int i = 0; i < index; i++) { - left[i] = v[i]; + left.write[i] = v[i]; } right.resize(v.size() - index); for (int i = 0; i < right.size(); i++) { - right[i] = v[index + i]; + right.write[i] = v[index + i]; } Vector<Vector2> r1 = rdp(left, optimization); Vector<Vector2> r2 = rdp(right, optimization); @@ -385,7 +385,7 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { int middle = r1.size(); r1.resize(r1.size() + r2.size()); for (int i = 0; i < r2.size(); i++) { - r1[middle + i] = r2[i]; + r1.write[middle + i] = r2[i]; } return r1; } else { @@ -412,37 +412,97 @@ static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, Vector2 last = result[result.size() - 1]; if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) { - result[0].y = last.y; + result.write[0].y = last.y; result.resize(result.size() - 1); } return result; } +struct FillBitsStackEntry { + Point2i pos; + int i; + int j; +}; + static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) { - for (int i = p_pos.x - 1; i <= p_pos.x + 1; i++) { - for (int j = p_pos.y - 1; j <= p_pos.y + 1; j++) { + // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps + PoolVector<FillBitsStackEntry> stack; + // Tracking size since we won't be shrinking the stack vector + int stack_size = 0; - if (i < rect.position.x || i >= rect.position.x + rect.size.x) - continue; - if (j < rect.position.y || j >= rect.position.y + rect.size.y) - continue; + Point2i pos = p_pos; + int next_i; + int next_j; - if (p_map->get_bit(Vector2(i, j))) - continue; + bool reenter = true; + bool popped = false; + do { + if (reenter) { + next_i = pos.x - 1; + next_j = pos.y - 1; + reenter = false; + } + + for (int i = next_i; i <= pos.x + 1; i++) { + for (int j = next_j; j <= pos.y + 1; j++) { + if (popped) { + // The next loop over j must start normally + next_j = pos.y; + popped = false; + // Skip because an iteration was already executed with current counter values + continue; + } - else if (p_src->get_bit(Vector2(i, j))) { - p_map->set_bit(Vector2(i, j), true); - fill_bits(p_src, p_map, Point2i(i, j), rect); + if (i < rect.position.x || i >= rect.position.x + rect.size.x) + continue; + if (j < rect.position.y || j >= rect.position.y + rect.size.y) + continue; + + if (p_map->get_bit(Vector2(i, j))) + continue; + + else if (p_src->get_bit(Vector2(i, j))) { + p_map->set_bit(Vector2(i, j), true); + + FillBitsStackEntry se = { pos, i, j }; + stack.resize(MAX(stack_size + 1, stack.size())); + stack.set(stack_size, se); + stack_size++; + + pos = Point2i(i, j); + reenter = true; + break; + } + } + if (reenter) { + break; } } - } + if (!reenter) { + if (stack_size) { + FillBitsStackEntry se = stack.get(stack_size - 1); + stack_size--; + pos = se.pos; + next_i = se.i; + next_j = se.j; + popped = true; + } + } + } while (reenter || popped); + +#ifdef DEBUG_ENABLED + print_line("max stack size: " + itos(stack.size())); +#endif } + Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); +#ifdef DEBUG_ENABLED print_line("Rect: " + r); +#endif Point2i from; Ref<BitMap> fill; fill.instance(); @@ -454,9 +514,13 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); +#ifdef DEBUG_ENABLED print_line("pre reduce: " + itos(polygon.size())); +#endif polygon = reduce(polygon, r, p_epsilon); +#ifdef DEBUG_ENABLED print_line("post reduce: " + itos(polygon.size())); +#endif polygons.push_back(polygon); fill_bits(this, fill, Point2i(j, i), r); } @@ -510,6 +574,34 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { } } +Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { + + Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon); + + // Convert result to bindable types + + Array result_array; + result_array.resize(result.size()); + for (int i = 0; i < result.size(); i++) { + + const Vector<Vector2> &polygon = result[i]; + + PoolVector2Array polygon_array; + polygon_array.resize(polygon.size()); + + { + PoolVector2Array::Write w = polygon_array.write(); + for (int j = 0; j < polygon.size(); j++) { + w[j] = polygon[j]; + } + } + + result_array[i] = polygon_array; + } + + return result_array; +} + void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); @@ -526,6 +618,9 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); + ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); + ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0)); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h index dcd5edb4fb..40f0bfb04a 100644 --- a/scene/resources/bit_mask.h +++ b/scene/resources/bit_mask.h @@ -46,6 +46,8 @@ class BitMap : public Resource { Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const; + Array _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; + protected: void _set_data(const Dictionary &p_d); Dictionary _get_data() const; diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index b2f586d02d..4a43303d84 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -40,10 +40,10 @@ Gradient::Gradient() { //Set initial color ramp transition from black to white points.resize(2); - points[0].color = Color(0, 0, 0, 1); - points[0].offset = 0; - points[1].color = Color(1, 1, 1, 1); - points[1].offset = 1; + points.write[0].color = Color(0, 0, 0, 1); + points.write[0].offset = 0; + points.write[1].color = Color(1, 1, 1, 1); + points.write[1].offset = 1; is_sorted = true; } @@ -79,7 +79,7 @@ Vector<float> Gradient::get_offsets() const { Vector<float> offsets; offsets.resize(points.size()); for (int i = 0; i < points.size(); i++) { - offsets[i] = points[i].offset; + offsets.write[i] = points[i].offset; } return offsets; } @@ -88,7 +88,7 @@ Vector<Color> Gradient::get_colors() const { Vector<Color> colors; colors.resize(points.size()); for (int i = 0; i < points.size(); i++) { - colors[i] = points[i].color; + colors.write[i] = points[i].color; } return colors; } @@ -96,7 +96,7 @@ Vector<Color> Gradient::get_colors() const { void Gradient::set_offsets(const Vector<float> &p_offsets) { points.resize(p_offsets.size()); for (int i = 0; i < points.size(); i++) { - points[i].offset = p_offsets[i]; + points.write[i].offset = p_offsets[i]; } is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); @@ -107,7 +107,7 @@ void Gradient::set_colors(const Vector<Color> &p_colors) { is_sorted = false; points.resize(p_colors.size()); for (int i = 0; i < points.size(); i++) { - points[i].color = p_colors[i]; + points.write[i].color = p_colors[i]; } emit_signal(CoreStringNames::get_singleton()->changed); } @@ -144,7 +144,7 @@ void Gradient::set_points(Vector<Gradient::Point> &p_points) { void Gradient::set_offset(int pos, const float offset) { if (points.size() <= pos) points.resize(pos + 1); - points[pos].offset = offset; + points.write[pos].offset = offset; is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -160,7 +160,7 @@ void Gradient::set_color(int pos, const Color &color) { points.resize(pos + 1); is_sorted = false; } - points[pos].color = color; + points.write[pos].color = color; emit_signal(CoreStringNames::get_singleton()->changed); } diff --git a/scene/resources/color_ramp.h b/scene/resources/color_ramp.h index c042a0d3d0..070ad7f0d3 100644 --- a/scene/resources/color_ramp.h +++ b/scene/resources/color_ramp.h @@ -98,7 +98,7 @@ public: while (low <= high) { middle = (low + high) / 2; - Point &point = points[middle]; + const Point &point = points[middle]; if (point.offset > p_offset) { high = middle - 1; //search low end of array } else if (point.offset < p_offset) { @@ -118,8 +118,8 @@ public: return points[points.size() - 1].color; if (first < 0) return points[0].color; - Point &pointFirst = points[first]; - Point &pointSecond = points[second]; + const Point &pointFirst = points[first]; + const Point &pointSecond = points[second]; return pointFirst.color.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); } diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp index 935f041837..bc9e2848b3 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -56,8 +56,8 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() { int idx = 0; for (Set<DrawEdge>::Element *E = edges.front(); E; E = E->next()) { - points[idx + 0] = E->get().a; - points[idx + 1] = E->get().b; + points.write[idx + 0] = E->get().a; + points.write[idx + 1] = E->get().b; idx += 2; } diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index a2e0996996..fa9369d3bc 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -46,8 +46,8 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { Vector<Vector3> lines; lines.resize(md.edges.size() * 2); for (int i = 0; i < md.edges.size(); i++) { - lines[i * 2 + 0] = md.vertices[md.edges[i].a]; - lines[i * 2 + 1] = md.vertices[md.edges[i].b]; + lines.write[i * 2 + 0] = md.vertices[md.edges[i].a]; + lines.write[i * 2 + 1] = md.vertices[md.edges[i].b]; } return lines; } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index f2fd919f20..d8989bf062 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -153,25 +153,25 @@ void Curve::clean_dupes() { void Curve::set_point_left_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].left_tangent = tangent; - _points[i].left_mode = TANGENT_FREE; + _points.write[i].left_tangent = tangent; + _points.write[i].left_mode = TANGENT_FREE; mark_dirty(); } void Curve::set_point_right_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].right_tangent = tangent; - _points[i].right_mode = TANGENT_FREE; + _points.write[i].right_tangent = tangent; + _points.write[i].right_mode = TANGENT_FREE; mark_dirty(); } void Curve::set_point_left_mode(int i, TangentMode p_mode) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].left_mode = p_mode; + _points.write[i].left_mode = p_mode; if (i > 0) { if (p_mode == TANGENT_LINEAR) { Vector2 v = (_points[i - 1].pos - _points[i].pos).normalized(); - _points[i].left_tangent = v.y / v.x; + _points.write[i].left_tangent = v.y / v.x; } } mark_dirty(); @@ -179,11 +179,11 @@ void Curve::set_point_left_mode(int i, TangentMode p_mode) { void Curve::set_point_right_mode(int i, TangentMode p_mode) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].right_mode = p_mode; + _points.write[i].right_mode = p_mode; if (i + 1 < _points.size()) { if (p_mode == TANGENT_LINEAR) { Vector2 v = (_points[i + 1].pos - _points[i].pos).normalized(); - _points[i].right_tangent = v.y / v.x; + _points.write[i].right_tangent = v.y / v.x; } } mark_dirty(); @@ -222,7 +222,7 @@ void Curve::clear_points() { void Curve::set_point_value(int p_index, real_t pos) { ERR_FAIL_INDEX(p_index, _points.size()); - _points[p_index].pos.y = pos; + _points.write[p_index].pos.y = pos; update_auto_tangents(p_index); mark_dirty(); } @@ -232,10 +232,10 @@ int Curve::set_point_offset(int p_index, float offset) { Point p = _points[p_index]; remove_point(p_index); int i = add_point(Vector2(offset, p.pos.y)); - _points[i].left_tangent = p.left_tangent; - _points[i].right_tangent = p.right_tangent; - _points[i].left_mode = p.left_mode; - _points[i].right_mode = p.right_mode; + _points.write[i].left_tangent = p.left_tangent; + _points.write[i].right_tangent = p.right_tangent; + _points.write[i].left_mode = p.left_mode; + _points.write[i].right_mode = p.right_mode; if (p_index != i) update_auto_tangents(p_index); update_auto_tangents(i); @@ -254,7 +254,7 @@ Curve::Point Curve::get_point(int p_index) const { void Curve::update_auto_tangents(int i) { - Point &p = _points[i]; + Point &p = _points.write[i]; if (i > 0) { if (p.left_mode == TANGENT_LINEAR) { @@ -263,7 +263,7 @@ void Curve::update_auto_tangents(int i) { } if (_points[i - 1].right_mode == TANGENT_LINEAR) { Vector2 v = (_points[i - 1].pos - p.pos).normalized(); - _points[i - 1].right_tangent = v.y / v.x; + _points.write[i - 1].right_tangent = v.y / v.x; } } @@ -274,7 +274,7 @@ void Curve::update_auto_tangents(int i) { } if (_points[i + 1].left_mode == TANGENT_LINEAR) { Vector2 v = (_points[i + 1].pos - p.pos).normalized(); - _points[i + 1].left_tangent = v.y / v.x; + _points.write[i + 1].left_tangent = v.y / v.x; } } } @@ -402,7 +402,7 @@ void Curve::set_data(Array input) { for (int j = 0; j < _points.size(); ++j) { - Point &p = _points[j]; + Point &p = _points.write[j]; int i = j * ELEMS; p.pos = input[i]; @@ -426,12 +426,12 @@ void Curve::bake() { for (int i = 1; i < _bake_resolution - 1; ++i) { real_t x = i / static_cast<real_t>(_bake_resolution); real_t y = interpolate(x); - _baked_cache[i] = y; + _baked_cache.write[i] = y; } if (_points.size() != 0) { - _baked_cache[0] = _points[0].pos.y; - _baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y; + _baked_cache.write[0] = _points[0].pos.y; + _baked_cache.write[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y; } _baked_cache_dirty = false; @@ -553,7 +553,7 @@ void Curve2D::set_point_position(int p_index, const Vector2 &p_pos) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].pos = p_pos; + points.write[p_index].pos = p_pos; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -567,7 +567,7 @@ void Curve2D::set_point_in(int p_index, const Vector2 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].in = p_in; + points.write[p_index].in = p_in; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -581,7 +581,7 @@ void Curve2D::set_point_out(int p_index, const Vector2 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].out = p_out; + points.write[p_index].out = p_out; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -930,9 +930,9 @@ void Curve2D::_set_data(const Dictionary &p_data) { for (int i = 0; i < points.size(); i++) { - points[i].in = r[i * 3 + 0]; - points[i].out = r[i * 3 + 1]; - points[i].pos = r[i * 3 + 2]; + points.write[i].in = r[i * 3 + 0]; + points.write[i].out = r[i * 3 + 1]; + points.write[i].pos = r[i * 3 + 2]; } baked_cache_dirty = true; @@ -952,7 +952,7 @@ PoolVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) const int pc = 1; for (int i = 0; i < points.size() - 1; i++) { - _bake_segment2d(midpoints[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); + _bake_segment2d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); pc++; pc += midpoints[i].size(); } @@ -1049,7 +1049,7 @@ void Curve3D::set_point_position(int p_index, const Vector3 &p_pos) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].pos = p_pos; + points.write[p_index].pos = p_pos; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1063,7 +1063,7 @@ void Curve3D::set_point_tilt(int p_index, float p_tilt) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].tilt = p_tilt; + points.write[p_index].tilt = p_tilt; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1077,7 +1077,7 @@ void Curve3D::set_point_in(int p_index, const Vector3 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].in = p_in; + points.write[p_index].in = p_in; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1091,7 +1091,7 @@ void Curve3D::set_point_out(int p_index, const Vector3 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].out = p_out; + points.write[p_index].out = p_out; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1621,10 +1621,10 @@ void Curve3D::_set_data(const Dictionary &p_data) { for (int i = 0; i < points.size(); i++) { - points[i].in = r[i * 3 + 0]; - points[i].out = r[i * 3 + 1]; - points[i].pos = r[i * 3 + 2]; - points[i].tilt = rt[i]; + points.write[i].in = r[i * 3 + 0]; + points.write[i].out = r[i * 3 + 1]; + points.write[i].pos = r[i * 3 + 2]; + points.write[i].tilt = rt[i]; } baked_cache_dirty = true; @@ -1644,7 +1644,7 @@ PoolVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) const int pc = 1; for (int i = 0; i < points.size() - 1; i++) { - _bake_segment3d(midpoints[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); + _bake_segment3d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); pc++; pc += midpoints[i].size(); } diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index fe12e2f5f6..2d05f5e19f 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -434,9 +434,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0)); theme->set_color("cursor_color", "LineEdit", control_font_color_hover); theme->set_color("selection_color", "LineEdit", font_color_selection); + theme->set_color("clear_button_color", "LineEdit", control_font_color); + theme->set_color("clear_button_color_pressed", "LineEdit", control_font_color_pressed); theme->set_constant("minimum_spaces", "LineEdit", 12 * scale); + theme->set_icon("clear", "LineEdit", make_icon(line_edit_clear_png)); + // ProgressBar theme->set_stylebox("bg", "ProgressBar", make_stylebox(progress_bar_png, 4, 4, 4, 4, 0, 0, 0, 0)); @@ -883,6 +887,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("logo", "Icons", make_icon(logo_png)); + // Visual Node Ports + theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); + theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + // Theme default_icon = make_icon(error_icon_png); diff --git a/scene/resources/default_theme/line_edit_clear.png b/scene/resources/default_theme/line_edit_clear.png Binary files differnew file mode 100644 index 0000000000..af2775a132 --- /dev/null +++ b/scene/resources/default_theme/line_edit_clear.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index c6b37cad5a..353e7eddbe 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -3,35 +3,35 @@ // png image block static const unsigned char arrow_down_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x56, 0x75, 0x5c, 0xe7, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x6d, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0x63, 0x60, 0x18, 0x74, 0x80, 0x11, 0xc6, 0x78, 0xf0, 0xe0, 0xc1, 0x7f, 0x7c, 0xa, 0x15, 0x14, 0x14, 0x18, 0x19, 0x18, 0x18, 0x18, 0x98, 0x48, 0xb5, 0x81, 0x7c, 0xd, 0x8c, 0x8c, 0x8c, 0xf1, 0xc, 0xc, 0xc, 0x5f, 0xb1, 0xa8, 0xf9, 0xce, 0xc8, 0xc8, 0x98, 0xe, 0x57, 0x87, 0x2c, 0x73, 0xff, 0xfe, 0x7d, 0xd, 0x6, 0x6, 0x86, 0x55, 0x8c, 0x8c, 0x8c, 0xba, 0x50, 0xa1, 0x1b, 0xcc, 0xcc, 0xcc, 0x61, 0xb2, 0xb2, 0xb2, 0x97, 0xb1, 0x6a, 0x60, 0x60, 0x60, 0x60, 0x78, 0xf1, 0xe2, 0x5, 0xf7, 0xcf, 0x9f, 0x3f, 0xa7, 0x30, 0x30, 0x30, 0x30, 0xb0, 0xb3, 0xb3, 0xe7, 0x48, 0x48, 0x48, 0x60, 0xb3, 0x75, 0x30, 0x1, 0x0, 0x28, 0x20, 0x14, 0xc2, 0x1b, 0xd0, 0x7c, 0xca, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x34, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x32, 0x78, 0xf0, 0x1f, 0x15, 0x52, 0x20, 0xf1, 0x30, 0xee, 0xc1, 0x17, 0xb8, 0xf0, 0xb7, 0x87, 0x69, 0x48, 0xb6, 0xdc, 0xd7, 0xb8, 0x7f, 0x9, 0x2c, 0x7c, 0xfd, 0xb1, 0x2e, 0x9a, 0x3, 0x5e, 0x70, 0x3f, 0x9c, 0xff, 0x70, 0xfe, 0xb, 0x6e, 0x6, 0xea, 0x3, 0x0, 0xfb, 0x81, 0x48, 0xb8, 0x4d, 0xe4, 0x75, 0xd9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char arrow_right_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x56, 0x75, 0x5c, 0xe7, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x6c, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0x63, 0x60, 0xa0, 0x35, 0x60, 0x44, 0xe6, 0x3c, 0x78, 0xf0, 0xe0, 0x3f, 0x8c, 0xfd, 0xff, 0xff, 0xff, 0x44, 0x45, 0x45, 0xc5, 0x5, 0xe8, 0x1a, 0x98, 0x70, 0x9a, 0xc4, 0xc8, 0x38, 0xe5, 0xe1, 0xc3, 0x87, 0xda, 0x44, 0x6b, 0x60, 0x60, 0x60, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0xaa, 0x17, 0x2f, 0x5e, 0x70, 0x13, 0xab, 0x81, 0x81, 0x81, 0x81, 0x41, 0xeb, 0xe7, 0xcf, 0x9f, 0x53, 0x48, 0xd1, 0x80, 0x1, 0x8, 0x69, 0xb8, 0xc6, 0xce, 0xce, 0x9e, 0x43, 0xac, 0x86, 0xaf, 0x8c, 0x8c, 0x8c, 0x61, 0x12, 0x12, 0x12, 0x5f, 0x89, 0xd2, 0xf0, 0xff, 0xff, 0xff, 0x1c, 0x79, 0x79, 0xf9, 0xab, 0x84, 0x1d, 0x49, 0x6d, 0x0, 0x0, 0x8f, 0x30, 0x1e, 0x10, 0x6e, 0x79, 0xda, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char background_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x44, 0xa4, 0x8a, 0xc6, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x11, 0x50, 0x4c, 0x54, 0x45, 0x91, 0xc9, 0xab, 0x90, 0xc9, 0xab, 0x90, 0xc9, 0xaa, 0x90, 0xc8, 0xab, 0x91, 0xc9, 0xaa, 0x91, 0xc8, 0xab, 0x90, 0xc8, 0xaa, 0x8f, 0xc8, 0xab, 0x8f, 0xc9, 0xab, 0x8f, 0xc8, 0xaa, 0x90, 0xc7, 0xaa, 0x90, 0xc7, 0xab, 0x8f, 0xc7, 0xaa, 0x8f, 0xc7, 0xab, 0x8e, 0xc7, 0xab, 0x8e, 0xc6, 0xab, 0x8f, 0xc6, 0xab, 0x8e, 0xc6, 0xaa, 0x8f, 0xc6, 0xaa, 0x8e, 0xc7, 0xaa, 0x8e, 0xc5, 0xaa, 0x8e, 0xc5, 0xab, 0x8d, 0xc5, 0xaa, 0x8d, 0xc5, 0xab, 0x8d, 0xc6, 0xaa, 0x8d, 0xc6, 0xab, 0x8d, 0xc4, 0xaa, 0x8e, 0xc4, 0xab, 0x8d, 0xc4, 0xab, 0x8e, 0xc4, 0xaa, 0x8c, 0xc4, 0xaa, 0x8c, 0xc5, 0xaa, 0x8d, 0xc3, 0xab, 0x8d, 0xc3, 0xaa, 0x8c, 0xc3, 0xaa, 0x8c, 0xc4, 0xab, 0x8c, 0xc3, 0xab, 0x8c, 0xc2, 0xab, 0x8b, 0xc2, 0xaa, 0x8b, 0xc3, 0xaa, 0x8b, 0xc3, 0xab, 0x8c, 0xc2, 0xaa, 0x8b, 0xc2, 0xab, 0x8b, 0xc1, 0xaa, 0x8b, 0xc1, 0xab, 0x8a, 0xc2, 0xaa, 0x8a, 0xc1, 0xaa, 0x8a, 0xc0, 0xaa, 0x8b, 0xc0, 0xaa, 0x8a, 0xc1, 0xa9, 0x8a, 0xc0, 0xa9, 0x89, 0xc0, 0xaa, 0x8a, 0xbf, 0xaa, 0x89, 0xbf, 0xaa, 0x89, 0xbf, 0xa9, 0x8a, 0xbf, 0xa9, 0x88, 0xbf, 0xaa, 0x89, 0xbe, 0xaa, 0x89, 0xbe, 0xa9, 0x88, 0xbf, 0xa9, 0x88, 0xbe, 0xa9, 0x88, 0xbe, 0xaa, 0x88, 0xbd, 0xaa, 0x88, 0xbd, 0xa9, 0x89, 0xbd, 0xaa, 0x89, 0xbd, 0xa9, 0x87, 0xbe, 0xa9, 0x87, 0xbd, 0xaa, 0x87, 0xbe, 0xaa, 0x87, 0xbd, 0xa9, 0x87, 0xbc, 0xaa, 0x88, 0xbc, 0xa9, 0x88, 0xbc, 0xaa, 0x87, 0xbc, 0xa9, 0x86, 0xbc, 0xa9, 0x87, 0xbb, 0xaa, 0x87, 0xbb, 0xa9, 0x86, 0xbb, 0xa9, 0x86, 0xbc, 0xaa, 0x86, 0xbb, 0xaa, 0x86, 0xba, 0xaa, 0x86, 0xba, 0xa9, 0x85, 0xba, 0xa9, 0x85, 0xbb, 0xaa, 0x85, 0xbb, 0xa9, 0x85, 0xba, 0xaa, 0x85, 0xb9, 0xa9, 0x86, 0xb9, 0xa9, 0x86, 0xb9, 0xaa, 0x85, 0xb9, 0xaa, 0xff, 0xff, 0xff, 0x25, 0xe, 0xc5, 0xe1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x5a, 0x3, 0xbb, 0xa5, 0xa2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x2, 0xaa, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x45, 0x4b, 0xd, 0x63, 0x9a, 0x56, 0x14, 0xbd, 0x86, 0x2d, 0x24, 0x2b, 0xf8, 0x8a, 0x1, 0xcc, 0x18, 0x23, 0xcd, 0x8b, 0xc5, 0x2a, 0xa9, 0x6e, 0x51, 0x98, 0x58, 0x3e, 0xda, 0x52, 0x69, 0x9e, 0x4a, 0x10, 0xba, 0xb8, 0xec, 0xff, 0xff, 0x91, 0x9d, 0x47, 0xd2, 0xf5, 0xbc, 0xcb, 0x3d, 0x1f, 0xf7, 0x40, 0xd4, 0x23, 0x4c, 0x4f, 0x2, 0x9b, 0xa8, 0x93, 0x27, 0x9d, 0xa3, 0xe7, 0x50, 0xe9, 0xc9, 0x79, 0x69, 0x74, 0xf7, 0x9f, 0x7a, 0x8a, 0xd2, 0xfb, 0xf9, 0xf4, 0x25, 0x53, 0xd5, 0x33, 0xf4, 0xcf, 0xce, 0x90, 0x9d, 0xa8, 0xa, 0x80, 0x75, 0xaa, 0xc8, 0xca, 0xa9, 0xa2, 0x9c, 0xf4, 0x54, 0x52, 0x91, 0x9c, 0xab, 0x2a, 0x46, 0x42, 0xf9, 0xe5, 0xb4, 0x2b, 0xa9, 0xe7, 0xca, 0xab, 0x57, 0xc8, 0x15, 0x8d, 0x74, 0x4d, 0xd5, 0x25, 0x40, 0x8a, 0xae, 0xea, 0xda, 0xb9, 0xd6, 0x59, 0x55, 0x12, 0xfe, 0xd0, 0x49, 0xef, 0xb3, 0xbe, 0xfe, 0x5a, 0x37, 0x34, 0x66, 0x30, 0x5d, 0xd3, 0xfb, 0xfa, 0x80, 0x69, 0x3, 0x7d, 0x70, 0xd1, 0xd5, 0xfa, 0x9a, 0x4e, 0x3, 0xe3, 0x62, 0xd0, 0xd7, 0xd, 0xd6, 0x37, 0x98, 0xd1, 0xef, 0xf, 0xc0, 0x9a, 0xc1, 0x2e, 0xc, 0xc6, 0x2e, 0xd8, 0xeb, 0x1, 0x86, 0x98, 0x61, 0x32, 0x13, 0x7f, 0x33, 0x66, 0x49, 0x36, 0xba, 0x41, 0x68, 0x18, 0xdd, 0x66, 0x64, 0x5b, 0x96, 0x69, 0x59, 0x90, 0xd6, 0xf0, 0x72, 0x68, 0x9a, 0x43, 0xd3, 0xfa, 0xd5, 0x32, 0x2f, 0x6d, 0xe3, 0xd2, 0xb2, 0x98, 0x65, 0x30, 0x93, 0x1c, 0x7b, 0x88, 0x67, 0xdb, 0xa6, 0xd, 0x72, 0x7e, 0x1b, 0xba, 0xa8, 0xc0, 0xda, 0xbf, 0xf, 0x91, 0xba, 0xb6, 0x4b, 0x8e, 0xe7, 0x78, 0xee, 0xd0, 0x75, 0x87, 0x1e, 0xac, 0xe3, 0x60, 0x5f, 0x39, 0x57, 0xe, 0x84, 0xe7, 0xd8, 0xb6, 0x63, 0x5f, 0xd1, 0x1b, 0xe7, 0x9a, 0xcb, 0x92, 0xe7, 0xba, 0xae, 0xe7, 0xf0, 0x1b, 0xc7, 0xb9, 0x76, 0x3c, 0x8e, 0xb3, 0x87, 0x16, 0xbf, 0xf1, 0x88, 0x73, 0xee, 0x8c, 0x46, 0xd8, 0xd7, 0x7c, 0xe4, 0xf1, 0xef, 0x18, 0x79, 0x37, 0x9c, 0x7b, 0x6f, 0xbc, 0x11, 0x27, 0xfe, 0xd6, 0xf7, 0xf9, 0x98, 0xf3, 0x77, 0x13, 0xfe, 0x96, 0x8f, 0xb8, 0x3f, 0x96, 0x65, 0x9f, 0x4f, 0x38, 0x1f, 0x4f, 0xf8, 0x68, 0xc2, 0xc9, 0x7f, 0xc6, 0x74, 0xea, 0x73, 0x49, 0x13, 0xcc, 0x78, 0xe2, 0xfb, 0x13, 0x59, 0xf2, 0xdf, 0xf9, 0x63, 0xa, 0xa6, 0xfe, 0x34, 0xf0, 0xf1, 0xba, 0xe9, 0x20, 0x39, 0xf0, 0x6f, 0x83, 0xdb, 0x69, 0x10, 0x4c, 0x29, 0x78, 0x3f, 0xb, 0x50, 0x91, 0xe7, 0xc0, 0x9f, 0x61, 0xdd, 0x6, 0xb3, 0xd9, 0xcc, 0xf7, 0xdf, 0x83, 0xe0, 0x2, 0x9a, 0xcd, 0x67, 0xcf, 0x98, 0x77, 0xea, 0xf, 0x29, 0xe4, 0xfc, 0x9, 0x13, 0x40, 0xd0, 0x1c, 0x87, 0x1f, 0xb8, 0x93, 0xab, 0xb, 0xee, 0x5e, 0x62, 0x5a, 0x2c, 0xe7, 0x21, 0x78, 0xb9, 0x58, 0x2e, 0xb1, 0xd1, 0xb8, 0x5b, 0x84, 0x4b, 0xd8, 0xf9, 0x7c, 0x81, 0xef, 0x2e, 0xa4, 0xc5, 0x22, 0xc, 0x97, 0x61, 0x18, 0x85, 0x12, 0x51, 0x4, 0x5e, 0x46, 0x61, 0xf4, 0x97, 0x74, 0xb, 0xb9, 0xa8, 0x3b, 0xad, 0xe2, 0x38, 0xa, 0xd7, 0xab, 0x28, 0x4, 0x85, 0x1f, 0xe2, 0x10, 0x1c, 0xc5, 0xe1, 0x2a, 0x5c, 0x25, 0x2b, 0x4a, 0xe3, 0x38, 0x49, 0x93, 0x24, 0x4d, 0xe3, 0x34, 0x4e, 0xe2, 0x68, 0xd, 0x1f, 0xad, 0x93, 0x24, 0x59, 0x23, 0xfa, 0xb0, 0x4e, 0x53, 0xca, 0xf2, 0x2c, 0xfb, 0xf8, 0xf1, 0x13, 0x6c, 0x92, 0x42, 0xa7, 0x49, 0x9e, 0xe6, 0x69, 0x92, 0x49, 0xd3, 0x65, 0x94, 0x7d, 0xce, 0xb2, 0x2c, 0xc9, 0xd2, 0xa2, 0xf8, 0x92, 0xe7, 0x59, 0x51, 0xc0, 0x7d, 0xce, 0xb, 0xc8, 0x1c, 0xc8, 0x36, 0x5, 0x95, 0x5f, 0xb3, 0x72, 0x53, 0x66, 0xf7, 0x5f, 0x37, 0x70, 0xf7, 0x79, 0x51, 0x88, 0xfb, 0x12, 0x3d, 0x91, 0xdd, 0x8b, 0x4d, 0x99, 0x8b, 0x92, 0x44, 0x59, 0xa, 0x51, 0x8a, 0x72, 0xb, 0x2e, 0x37, 0x2, 0x1a, 0xa2, 0x0, 0x8b, 0xd, 0x62, 0x21, 0x48, 0xec, 0x76, 0x7b, 0x21, 0xaa, 0x52, 0xec, 0xb6, 0xa2, 0xaa, 0xb6, 0xf, 0xfb, 0x7d, 0x2d, 0xc4, 0xf6, 0xa1, 0x7a, 0x10, 0xdb, 0x9d, 0x28, 0x77, 0x15, 0x55, 0xbb, 0xba, 0x92, 0xd8, 0x57, 0xbb, 0xaa, 0xaa, 0xeb, 0x7d, 0x2d, 0xed, 0xff, 0xdf, 0xbe, 0xa6, 0x43, 0xd5, 0x34, 0x75, 0xd3, 0x54, 0xcd, 0xa1, 0xaa, 0xf, 0x75, 0x83, 0xbc, 0xae, 0x5a, 0xb4, 0x64, 0xf6, 0xd, 0x1d, 0x6a, 0x25, 0x9a, 0xa6, 0x6d, 0xab, 0x43, 0x3, 0xfc, 0x2d, 0x57, 0x8b, 0xf7, 0xf8, 0xd8, 0xb4, 0x87, 0xb6, 0x6e, 0xe9, 0xb1, 0x6d, 0x8e, 0x6d, 0x7b, 0x6c, 0xff, 0x39, 0x3e, 0x1d, 0x9f, 0x20, 0x8f, 0xd2, 0x1d, 0xff, 0x45, 0xa, 0xdd, 0xb6, 0x4f, 0xff, 0x1, 0xbe, 0xd3, 0xa6, 0xf7, 0x55, 0x9e, 0xe1, 0xf0, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x44, 0xa4, 0x8a, 0xc6, 0x0, 0x0, 0x1, 0xe, 0x50, 0x4c, 0x54, 0x45, 0x91, 0xc9, 0xab, 0x90, 0xc9, 0xab, 0x90, 0xc9, 0xaa, 0x90, 0xc8, 0xab, 0x91, 0xc9, 0xaa, 0x91, 0xc8, 0xab, 0x90, 0xc8, 0xaa, 0x8f, 0xc8, 0xab, 0x8f, 0xc9, 0xab, 0x8f, 0xc8, 0xaa, 0x90, 0xc7, 0xaa, 0x90, 0xc7, 0xab, 0x8f, 0xc7, 0xaa, 0x8f, 0xc7, 0xab, 0x8e, 0xc7, 0xab, 0x8e, 0xc6, 0xab, 0x8f, 0xc6, 0xab, 0x8e, 0xc6, 0xaa, 0x8f, 0xc6, 0xaa, 0x8e, 0xc7, 0xaa, 0x8e, 0xc5, 0xaa, 0x8e, 0xc5, 0xab, 0x8d, 0xc5, 0xaa, 0x8d, 0xc5, 0xab, 0x8d, 0xc6, 0xaa, 0x8d, 0xc6, 0xab, 0x8d, 0xc4, 0xaa, 0x8e, 0xc4, 0xab, 0x8d, 0xc4, 0xab, 0x8e, 0xc4, 0xaa, 0x8c, 0xc4, 0xaa, 0x8c, 0xc5, 0xaa, 0x8d, 0xc3, 0xab, 0x8d, 0xc3, 0xaa, 0x8c, 0xc3, 0xaa, 0x8c, 0xc4, 0xab, 0x8c, 0xc3, 0xab, 0x8c, 0xc2, 0xab, 0x8b, 0xc2, 0xaa, 0x8b, 0xc3, 0xaa, 0x8b, 0xc3, 0xab, 0x8c, 0xc2, 0xaa, 0x8b, 0xc2, 0xab, 0x8b, 0xc1, 0xaa, 0x8b, 0xc1, 0xab, 0x8a, 0xc2, 0xaa, 0x8a, 0xc1, 0xaa, 0x8a, 0xc0, 0xaa, 0x8b, 0xc0, 0xaa, 0x8a, 0xc1, 0xa9, 0x8a, 0xc0, 0xa9, 0x89, 0xc0, 0xaa, 0x8a, 0xbf, 0xaa, 0x89, 0xbf, 0xaa, 0x89, 0xbf, 0xa9, 0x8a, 0xbf, 0xa9, 0x88, 0xbf, 0xaa, 0x89, 0xbe, 0xaa, 0x89, 0xbe, 0xa9, 0x88, 0xbf, 0xa9, 0x88, 0xbe, 0xa9, 0x88, 0xbe, 0xaa, 0x88, 0xbd, 0xaa, 0x88, 0xbd, 0xa9, 0x89, 0xbd, 0xaa, 0x89, 0xbd, 0xa9, 0x87, 0xbe, 0xa9, 0x87, 0xbd, 0xaa, 0x87, 0xbe, 0xaa, 0x87, 0xbd, 0xa9, 0x87, 0xbc, 0xaa, 0x88, 0xbc, 0xa9, 0x88, 0xbc, 0xaa, 0x87, 0xbc, 0xa9, 0x86, 0xbc, 0xa9, 0x87, 0xbb, 0xaa, 0x87, 0xbb, 0xa9, 0x86, 0xbb, 0xa9, 0x86, 0xbc, 0xaa, 0x86, 0xbb, 0xaa, 0x86, 0xba, 0xaa, 0x86, 0xba, 0xa9, 0x85, 0xba, 0xa9, 0x85, 0xbb, 0xaa, 0x85, 0xbb, 0xa9, 0x85, 0xba, 0xaa, 0x85, 0xb9, 0xa9, 0x86, 0xb9, 0xa9, 0x86, 0xb9, 0xaa, 0x85, 0xb9, 0xaa, 0x3e, 0xa0, 0x4f, 0x4f, 0x0, 0x0, 0x2, 0x3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2d, 0x4a, 0x7, 0x82, 0x4, 0x37, 0x12, 0x2, 0xa, 0x4a, 0xea, 0x9e, 0xbd, 0xff, 0x3f, 0xf5, 0x6c, 0x4f, 0x4b, 0xda, 0x58, 0x91, 0x24, 0x10, 0x20, 0xf8, 0x5d, 0x20, 0x81, 0x1f, 0xa8, 0x1f, 0xf6, 0x7, 0xf5, 0x7d, 0x0, 0x0, 0x3c, 0x0, 0x8, 0x14, 0x28, 0xb8, 0x41, 0x0, 0x50, 0x7b, 0x70, 0x63, 0xc, 0x9, 0x1c, 0xaa, 0x52, 0xeb, 0x4, 0x5b, 0xfb, 0x1d, 0x89, 0x68, 0xe1, 0x39, 0x9c, 0x7d, 0x68, 0x77, 0x60, 0x8c, 0x7a, 0x80, 0xe5, 0x6e, 0xdc, 0x41, 0x47, 0x51, 0x5f, 0xb8, 0xdd, 0xed, 0xa0, 0x9f, 0xc6, 0xad, 0xba, 0xee, 0xfb, 0xee, 0xde, 0x97, 0x74, 0x6f, 0x98, 0x82, 0x26, 0x51, 0x30, 0xa6, 0x3f, 0x10, 0xc8, 0xad, 0x13, 0xe4, 0x83, 0x0, 0x7a, 0x4, 0x4a, 0xd9, 0x5c, 0xde, 0xd2, 0x16, 0x15, 0xf2, 0xac, 0xac, 0x3e, 0x8f, 0x5a, 0x15, 0x3a, 0x5a, 0xe0, 0x6a, 0x1, 0xdc, 0xbd, 0x9b, 0x47, 0x44, 0xb6, 0x1e, 0x44, 0x5b, 0xe7, 0x84, 0x4b, 0xc3, 0xc, 0x1b, 0x51, 0xeb, 0xaa, 0x54, 0xc7, 0xb3, 0xeb, 0xca, 0x1a, 0x36, 0xbc, 0x76, 0x64, 0x4a, 0x62, 0x54, 0x3c, 0xe7, 0xf4, 0xc0, 0xd4, 0x1b, 0xda, 0x48, 0xa4, 0xc1, 0xd7, 0x98, 0xc2, 0xac, 0x19, 0x76, 0xd7, 0x44, 0x80, 0x33, 0xfe, 0xcb, 0x2b, 0x97, 0x6d, 0x8e, 0x2, 0x36, 0x4a, 0xfd, 0xc, 0xef, 0xb1, 0x35, 0x76, 0x63, 0x62, 0xc6, 0x5b, 0x38, 0xd3, 0x7e, 0x0, 0x7b, 0xfe, 0xef, 0x48, 0x93, 0xff, 0xdc, 0x3d, 0xed, 0xf4, 0xc4, 0xc4, 0x99, 0x93, 0xd3, 0x68, 0x5c, 0x9e, 0xee, 0x87, 0x16, 0xca, 0x1f, 0xbb, 0x80, 0x9e, 0xa0, 0x5, 0x47, 0x0, 0xbc, 0x5f, 0x7, 0x79, 0xa9, 0x3, 0x99, 0x80, 0x8f, 0xa7, 0x16, 0x68, 0xcd, 0x3a, 0x3d, 0x32, 0x60, 0xc0, 0x6f, 0xd8, 0x2b, 0x8a, 0xe1, 0x20, 0xf, 0xe3, 0x4, 0xcf, 0xfa, 0x1a, 0xb, 0x90, 0xfb, 0x2a, 0x57, 0x4a, 0xa8, 0xc9, 0xb7, 0x91, 0x37, 0x82, 0xf7, 0x4c, 0xd2, 0xa9, 0x9a, 0x78, 0x8f, 0x53, 0x22, 0x40, 0xfd, 0x44, 0x5c, 0xdf, 0xeb, 0x89, 0x4a, 0xc9, 0x0, 0xe7, 0x74, 0x1, 0x28, 0xff, 0xa6, 0x7e, 0xa, 0x4, 0xe9, 0x43, 0xe2, 0x7, 0x7d, 0xaf, 0x9, 0xca, 0x61, 0x3, 0x88, 0x13, 0x20, 0xab, 0x50, 0x1e, 0x41, 0xbc, 0x68, 0x3, 0xd5, 0xc2, 0x69, 0xa4, 0x5b, 0x22, 0xbb, 0x35, 0xdc, 0x45, 0x72, 0x94, 0xba, 0x85, 0xee, 0x82, 0x54, 0x0, 0xcb, 0x45, 0xa4, 0x78, 0xa, 0xc0, 0x28, 0x1c, 0x91, 0x46, 0x50, 0xa3, 0x34, 0xcb, 0x63, 0xfa, 0xeb, 0xb8, 0xd8, 0x5e, 0x9e, 0xde, 0x2b, 0xa3, 0xda, 0x35, 0xd3, 0x62, 0xc4, 0x8e, 0xca, 0x39, 0xf0, 0xb1, 0x7a, 0xd6, 0x69, 0x5f, 0x5, 0xa1, 0xa4, 0xa3, 0x3a, 0xdf, 0x8, 0xd8, 0xcf, 0x62, 0xf7, 0x14, 0x4f, 0x5a, 0x87, 0xa5, 0xc1, 0x22, 0x51, 0xe2, 0xd5, 0x9a, 0xc1, 0x1c, 0x37, 0x5e, 0xd6, 0x7f, 0xed, 0xfb, 0x41, 0x5e, 0xb7, 0xf, 0x7c, 0xe3, 0xba, 0x7b, 0xd0, 0xa5, 0x3a, 0xb3, 0x8c, 0xd7, 0x2e, 0x4e, 0xd7, 0xba, 0xbb, 0xd7, 0xc6, 0xb, 0x8d, 0x17, 0x1f, 0xe3, 0x46, 0x9, 0x49, 0xa1, 0x8c, 0x13, 0x63, 0x4c, 0xa6, 0xfa, 0x2a, 0x8c, 0x38, 0x88, 0x6a, 0xc9, 0x32, 0x4c, 0x1b, 0xa3, 0x44, 0x43, 0xd9, 0x55, 0xdb, 0xce, 0xc1, 0xe9, 0x92, 0x2f, 0x4a, 0x25, 0x59, 0x36, 0x52, 0x52, 0x41, 0xc4, 0x16, 0x2, 0x41, 0x32, 0x7a, 0x73, 0x4b, 0x21, 0xb, 0x8, 0x57, 0x89, 0xc2, 0x90, 0x65, 0xa8, 0xdc, 0x46, 0x56, 0x14, 0x15, 0x8e, 0xc1, 0x20, 0xd7, 0xcc, 0x40, 0x76, 0x42, 0x3a, 0x83, 0xf, 0x83, 0x46, 0xf5, 0x27, 0xa7, 0x80, 0x7e, 0xcf, 0xd2, 0x74, 0xd0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char base_green_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x1, 0x3, 0x0, 0x0, 0x0, 0x49, 0xb4, 0xe8, 0xb7, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x90, 0xc9, 0xab, 0xff, 0xff, 0xff, 0xc6, 0xd0, 0x9d, 0x30, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xc, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x18, 0xdc, 0x0, 0x0, 0x0, 0xa0, 0x0, 0x1, 0x61, 0x25, 0x7d, 0x47, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x1, 0x3, 0x0, 0x0, 0x0, 0x49, 0xb4, 0xe8, 0xb7, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x90, 0xc9, 0xab, 0xff, 0xff, 0xff, 0xc6, 0xd0, 0x9d, 0x30, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xe4, 0x0, 0x0, 0x0, 0xa0, 0x0, 0x1, 0xf3, 0xdb, 0xea, 0x79, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char button_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xd9, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x7d, 0x91, 0x41, 0x4e, 0xc3, 0x30, 0x14, 0x44, 0x9f, 0xed, 0x8f, 0x6d, 0x35, 0x8b, 0x9a, 0x6d, 0xb9, 0x6, 0xfb, 0xde, 0x81, 0xd3, 0xe4, 0x8, 0xf4, 0x32, 0xbd, 0x1, 0xb, 0x6e, 0xc0, 0x25, 0x60, 0x83, 0xa8, 0x2, 0x6a, 0xa4, 0xc6, 0x49, 0xfc, 0xbb, 0x88, 0x54, 0x11, 0x94, 0x30, 0xb3, 0xf9, 0xd2, 0x9f, 0x19, 0x8d, 0x34, 0x6, 0xc1, 0x13, 0x88, 0x78, 0x1c, 0x96, 0x9, 0x85, 0x91, 0xcc, 0x85, 0x8e, 0x2c, 0x78, 0x2a, 0x12, 0x5b, 0x2a, 0x2, 0x6, 0x3, 0x28, 0x4a, 0x47, 0xcb, 0x37, 0xd, 0x8, 0x81, 0xb4, 0x7f, 0x7a, 0xa8, 0x87, 0xc4, 0xc, 0xd2, 0xbc, 0x3f, 0xbf, 0x1e, 0x19, 0x84, 0xc8, 0x76, 0x57, 0x9f, 0xd2, 0x80, 0xfe, 0x7a, 0x1b, 0x24, 0xed, 0x6a, 0x5e, 0x38, 0xb, 0x9e, 0xaa, 0x4f, 0xfd, 0xdc, 0x8e, 0xd2, 0xd3, 0x27, 0x2a, 0xbc, 0xe0, 0x8, 0x65, 0xe6, 0xe6, 0xd6, 0x94, 0x80, 0x13, 0x2c, 0x46, 0x29, 0xb, 0x2, 0x5, 0x83, 0x15, 0xc0, 0xac, 0x26, 0x18, 0x90, 0xe9, 0x5c, 0x11, 0x30, 0x9, 0x54, 0x17, 0x5, 0xa, 0xa, 0x42, 0x41, 0xcb, 0x62, 0x87, 0x2, 0x4a, 0x11, 0x46, 0xba, 0xd5, 0x84, 0x8e, 0xd1, 0x92, 0x69, 0x6d, 0xe3, 0xd0, 0x3f, 0x74, 0xd8, 0x86, 0x96, 0xec, 0xb8, 0xc3, 0xa5, 0xaf, 0xfb, 0x47, 0x1f, 0x3d, 0xe1, 0xc6, 0x48, 0x3c, 0x7d, 0x1e, 0x3e, 0xde, 0xf8, 0x31, 0x6c, 0xfe, 0x1d, 0xab, 0x15, 0x32, 0x30, 0x70, 0x5e, 0x9b, 0xfb, 0xa, 0xbb, 0x7c, 0x61, 0xa2, 0x50, 0x44, 0x45, 0xca, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xc7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd0, 0x81, 0x66, 0x43, 0x31, 0x14, 0x87, 0xf1, 0xf, 0x5, 0x17, 0xb8, 0x28, 0x2e, 0x8, 0x71, 0xf3, 0x6, 0x19, 0xb6, 0xb9, 0xcb, 0xac, 0x95, 0xa4, 0xb7, 0xad, 0x6a, 0xd5, 0x68, 0x5f, 0xe4, 0x3e, 0x76, 0x1e, 0xe1, 0xbf, 0x21, 0xa6, 0xab, 0xf8, 0x1, 0x7c, 0x9c, 0x73, 0xe, 0xac, 0xe8, 0xe8, 0x19, 0x30, 0x58, 0xc6, 0xca, 0x62, 0x18, 0xe8, 0xe9, 0x58, 0x41, 0xc7, 0x1a, 0x87, 0x27, 0x10, 0x49, 0xe4, 0x5f, 0x89, 0x48, 0xc0, 0xe3, 0x58, 0xd3, 0x41, 0x8f, 0xb, 0xcb, 0xbd, 0x7c, 0xeb, 0xbf, 0x7b, 0x9, 0xb, 0x8e, 0x1e, 0x6, 0xfc, 0xad, 0x64, 0x6d, 0xb5, 0x79, 0xb0, 0x55, 0xd6, 0xad, 0xe0, 0x19, 0xc0, 0x10, 0xae, 0xda, 0x34, 0x5c, 0x45, 0xc0, 0x80, 0x25, 0x5e, 0xf4, 0xd5, 0x70, 0x11, 0x11, 0xb, 0x23, 0xe9, 0xac, 0xcf, 0x86, 0xb3, 0x48, 0x8c, 0x30, 0x92, 0x4f, 0xa, 0xd, 0x27, 0x91, 0x6b, 0x70, 0xd4, 0x47, 0xc3, 0xf1, 0x2f, 0x48, 0x7, 0x4d, 0xd, 0x87, 0x3a, 0xc2, 0x12, 0x67, 0xbd, 0x37, 0xcc, 0x75, 0x49, 0x43, 0xd8, 0xe9, 0xad, 0x61, 0x57, 0xcf, 0x1c, 0xf0, 0xfb, 0x32, 0xe9, 0xf5, 0xc9, 0xa4, 0x7d, 0x7d, 0x54, 0x8f, 0x7b, 0x59, 0xe6, 0x92, 0x14, 0x1f, 0x24, 0xcd, 0x3f, 0x7b, 0x6b, 0xa, 0xe, 0x6a, 0x82, 0x91, 0x45, 0x30, 0xba, 0x1, 0x4a, 0x51, 0xc4, 0x35, 0x1f, 0xe5, 0xa1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char button_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xb9, 0xa2, 0x9b, 0xc9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x3f, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc0, 0x4, 0x42, 0x26, 0xa1, 0xa1, 0xce, 0x8a, 0x40, 0x46, 0x7a, 0xe7, 0xcc, 0x99, 0x33, 0xca, 0x80, 0x8c, 0xd5, 0x20, 0xe1, 0x5d, 0x40, 0x1c, 0x3, 0x62, 0x1c, 0x5, 0xe2, 0x48, 0x10, 0x63, 0x2a, 0x7e, 0x6, 0x5c, 0x31, 0x58, 0xbb, 0x14, 0x10, 0xe7, 0xee, 0x9c, 0x39, 0x73, 0x1e, 0xc8, 0x40, 0x31, 0x98, 0x15, 0xe8, 0x0, 0x0, 0xec, 0xe0, 0x11, 0x6d, 0x2c, 0x6f, 0x45, 0x2b, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x62, 0xed, 0x5e, 0xfc, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0x84, 0x4c, 0x42, 0x43, 0x9d, 0x15, 0x81, 0x8c, 0xf4, 0xce, 0x99, 0x33, 0x67, 0x94, 0x1, 0x19, 0xab, 0x41, 0xc2, 0xbb, 0x80, 0x38, 0x6, 0xc4, 0x38, 0xa, 0xc4, 0x91, 0x20, 0xc6, 0x54, 0x3c, 0xc, 0xb8, 0x62, 0x98, 0x76, 0x29, 0x20, 0xce, 0xdd, 0x39, 0x73, 0xe6, 0x3c, 0x90, 0x81, 0x62, 0x10, 0x2b, 0x30, 0x1, 0x0, 0xec, 0xe0, 0x11, 0x6d, 0xb5, 0xe0, 0x8c, 0x99, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char button_hover_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x5f, 0x5a, 0x6b, 0x56, 0x53, 0x64, 0x57, 0x53, 0x64, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x57, 0x53, 0x63, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5b, 0x57, 0x66, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x43, 0x40, 0x4c, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0xff, 0xff, 0xff, 0x99, 0x8b, 0x2e, 0x55, 0x0, 0x0, 0x0, 0x16, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0x6b, 0x28, 0x52, 0x7a, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x2e, 0x54, 0xd3, 0x10, 0x87, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x97, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x65, 0xcf, 0x49, 0x12, 0x82, 0x30, 0x10, 0x5, 0xd0, 0xce, 0x48, 0x46, 0x40, 0x51, 0x4, 0x41, 0x45, 0xc4, 0x20, 0x88, 0xf7, 0xbf, 0x9e, 0x21, 0x45, 0x65, 0xa1, 0x6f, 0xd7, 0xbf, 0xaa, 0x27, 0x0, 0x84, 0x9, 0x65, 0xdc, 0x63, 0x94, 0x60, 0x4, 0x80, 0x12, 0x21, 0x95, 0x36, 0xd6, 0x1a, 0xad, 0xa4, 0x48, 0x10, 0x60, 0x91, 0x66, 0xe5, 0x29, 0x28, 0xb3, 0x54, 0x60, 0x20, 0x32, 0xaf, 0xea, 0x73, 0x50, 0x57, 0xb9, 0x24, 0x40, 0x55, 0xb3, 0xd5, 0x3e, 0x69, 0x14, 0x5, 0xa6, 0xdb, 0x4b, 0xd4, 0x6a, 0x6, 0xdc, 0x5c, 0x6f, 0xd1, 0xd5, 0x70, 0xe0, 0xb6, 0xbb, 0x47, 0x9d, 0x5d, 0x83, 0xfe, 0x11, 0xf5, 0x6b, 0x60, 0x86, 0x67, 0x34, 0xf8, 0x16, 0xa6, 0xdd, 0x18, 0x39, 0x3f, 0x94, 0x2a, 0x37, 0xbe, 0x36, 0xa3, 0xf3, 0x6b, 0x89, 0xdc, 0x4d, 0xf3, 0x3b, 0x98, 0xa7, 0xbd, 0x3f, 0xc, 0x8b, 0xe2, 0xb0, 0x7c, 0x82, 0xe5, 0x58, 0xf8, 0xd3, 0xff, 0x9e, 0xfb, 0x7d, 0xff, 0xb, 0x74, 0xeb, 0x15, 0x11, 0xe1, 0xa7, 0x60, 0xfc, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x5f, 0x5a, 0x6b, 0x56, 0x53, 0x64, 0x57, 0x53, 0x64, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x57, 0x53, 0x63, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5b, 0x57, 0x66, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x43, 0x40, 0x4c, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x40, 0xdc, 0xe6, 0x80, 0x0, 0x0, 0x0, 0x16, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0x6b, 0x28, 0x52, 0x7a, 0x0, 0x0, 0x0, 0x67, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xcf, 0x47, 0x2, 0x83, 0x30, 0x10, 0x43, 0x51, 0x69, 0x98, 0xf4, 0xe4, 0xfe, 0x87, 0x24, 0xfb, 0xf4, 0x8, 0xd1, 0xb1, 0x3f, 0xb8, 0xbd, 0xdd, 0x24, 0x38, 0x4, 0x40, 0x43, 0xc9, 0xfd, 0x24, 0x1a, 0x7a, 0x27, 0x2d, 0x7, 0xc2, 0x41, 0x2f, 0xbd, 0x69, 0x88, 0x23, 0x27, 0x38, 0x3d, 0xfd, 0x22, 0x2f, 0xd3, 0x1b, 0xc3, 0x35, 0xe9, 0x85, 0x39, 0xd, 0x10, 0x2c, 0x0, 0x25, 0x20, 0x81, 0x2d, 0xc0, 0xa0, 0x2d, 0x8, 0xa9, 0x12, 0x64, 0x0, 0x66, 0x91, 0xa5, 0x61, 0xf3, 0xbe, 0xc5, 0x18, 0xd9, 0x7e, 0x7e, 0x21, 0x45, 0x1b, 0x53, 0x77, 0x4a, 0xac, 0x87, 0x63, 0x3d, 0x7e, 0x7, 0x87, 0x7b, 0x3b, 0x5b, 0x7a, 0xd3, 0xea, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char button_normal_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x8a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0xff, 0xff, 0xff, 0xe5, 0x37, 0x10, 0x78, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x2d, 0xcd, 0xda, 0x41, 0x3d, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x93, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x65, 0xcf, 0x47, 0x12, 0x82, 0x40, 0x10, 0x5, 0xd0, 0x9e, 0xc8, 0x44, 0x92, 0x22, 0x41, 0x54, 0x44, 0x40, 0x14, 0xef, 0x7f, 0x3e, 0x7, 0x8a, 0xea, 0x85, 0xbe, 0xe5, 0xaf, 0xea, 0xf0, 0x1, 0x8, 0x65, 0x5c, 0xc8, 0x40, 0x70, 0x46, 0x9, 0x0, 0x89, 0x94, 0x36, 0xd6, 0x79, 0xef, 0xac, 0xd1, 0x2a, 0x22, 0x40, 0x55, 0x9c, 0x14, 0xa7, 0x4d, 0x91, 0xc4, 0x8a, 0x2, 0xd3, 0x69, 0x59, 0xd5, 0x9b, 0xaa, 0x4c, 0x35, 0x3, 0x6e, 0x9a, 0xfa, 0xbc, 0xab, 0x1b, 0xc3, 0x41, 0xd8, 0xf6, 0x82, 0x5a, 0x2b, 0x40, 0xba, 0xeb, 0xd, 0x5d, 0x9d, 0x4, 0xe9, 0xbb, 0x3b, 0xea, 0xfc, 0x1a, 0xf4, 0xf, 0xd4, 0xaf, 0x81, 0x1b, 0x46, 0x34, 0x84, 0x11, 0x61, 0xa7, 0x27, 0x9a, 0xc2, 0x52, 0x6e, 0xe6, 0x17, 0x9a, 0xc3, 0x59, 0xa6, 0xb3, 0xf1, 0xbd, 0x1b, 0xb3, 0xf0, 0x18, 0x55, 0xf9, 0x61, 0xf9, 0x6c, 0x96, 0x63, 0x1e, 0x5e, 0xff, 0x2b, 0xf7, 0x5b, 0xff, 0xb, 0x69, 0x5a, 0x14, 0xfa, 0x84, 0xf6, 0xc2, 0x8, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x14, 0x4, 0xd1, 0x2e, 0x1c, 0xee, 0x7f, 0xca, 0xd1, 0xed, 0x28, 0x8d, 0x4b, 0x92, 0x5a, 0xbe, 0xe8, 0x2f, 0xc4, 0x9c, 0x24, 0xcf, 0x15, 0x54, 0xab, 0x78, 0xee, 0x53, 0x30, 0x4a, 0x85, 0xa6, 0xfc, 0xf1, 0x87, 0x11, 0xb2, 0x9a, 0x15, 0x9a, 0x37, 0x13, 0x74, 0xce, 0xb4, 0xd4, 0x77, 0xcb, 0xe, 0xb4, 0x96, 0x99, 0x10, 0x34, 0x81, 0x42, 0x50, 0x21, 0x9d, 0x41, 0x23, 0xf8, 0xc, 0x56, 0xe1, 0x10, 0x9c, 0x40, 0x4e, 0xfe, 0x6e, 0x72, 0x96, 0x7e, 0xd7, 0xdf, 0x3f, 0xb3, 0x79, 0x90, 0xcd, 0xf1, 0xc4, 0x26, 0x1e, 0x8e, 0x78, 0xfc, 0x1, 0xf5, 0x61, 0x3f, 0x44, 0xe8, 0xf1, 0xdd, 0xba, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char button_pressed_png[] = { @@ -39,19 +39,19 @@ static const unsigned char button_pressed_png[] = { }; static const unsigned char checked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x12, 0x7b, 0xbc, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x8e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x65, 0xcf, 0x5b, 0x12, 0x82, 0x30, 0xc, 0x40, 0x51, 0x48, 0x5b, 0x15, 0x5, 0x5b, 0x42, 0x2b, 0x4, 0x50, 0x40, 0xc5, 0xb7, 0xee, 0x7f, 0x79, 0x96, 0x8a, 0x88, 0x7a, 0x3f, 0xcf, 0x4c, 0x26, 0x89, 0xe7, 0xfd, 0xe5, 0x3, 0xe3, 0xc2, 0x35, 0x99, 0x82, 0x6f, 0x1, 0x66, 0x81, 0x54, 0xae, 0x18, 0xe7, 0x60, 0x81, 0x5, 0x89, 0x36, 0x5d, 0xab, 0x34, 0x5b, 0x30, 0xb, 0x5c, 0x6a, 0xa2, 0xbc, 0x20, 0x2a, 0x71, 0x2d, 0xb9, 0x5, 0xa1, 0xc, 0x6d, 0xb0, 0xa2, 0x1a, 0x9b, 0xad, 0x12, 0x3d, 0xec, 0x70, 0xdf, 0x36, 0x58, 0x9b, 0x1, 0xe, 0xc7, 0xd3, 0x19, 0x4b, 0xfa, 0x80, 0x9d, 0xc9, 0xd2, 0xcb, 0x18, 0xae, 0x88, 0x37, 0x1a, 0x43, 0x91, 0xdf, 0x1f, 0x6f, 0x70, 0x6b, 0x5f, 0x69, 0xb7, 0x76, 0x38, 0xcc, 0xe8, 0x24, 0xec, 0xe, 0x83, 0x28, 0xec, 0x4f, 0x97, 0xcb, 0x8, 0xbe, 0x9e, 0xe3, 0xcc, 0x3d, 0xf7, 0xd3, 0x13, 0x10, 0x58, 0xd, 0x44, 0xd4, 0xa5, 0x38, 0x3e, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x8d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x58, 0x56, 0x63, 0xb0, 0xaf, 0xb5, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0xb6, 0xb6, 0xb9, 0x57, 0x57, 0x5a, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x8b, 0x8b, 0x8d, 0xff, 0xff, 0xff, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x42, 0x42, 0x47, 0xf8, 0xf8, 0xf8, 0xfe, 0xfe, 0xfe, 0x25, 0x25, 0x2a, 0x4e, 0x4e, 0x52, 0x26, 0x26, 0x2b, 0xc5, 0xc5, 0xc7, 0xaa, 0xaa, 0xab, 0xb8, 0xb8, 0xba, 0x5f, 0x5f, 0x63, 0x74, 0x74, 0x77, 0xed, 0xed, 0xed, 0x33, 0x33, 0x38, 0x8d, 0x8d, 0x8f, 0xb8, 0xb8, 0xb9, 0x35, 0x35, 0x39, 0x3a, 0x3a, 0x3e, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xb2, 0xb2, 0xb4, 0x45, 0x45, 0x49, 0x61, 0x61, 0x65, 0x8f, 0x8f, 0x92, 0x63, 0x63, 0x66, 0x2a, 0x2a, 0x2f, 0x40, 0x82, 0xb, 0xf6, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xfa, 0xdd, 0xfb, 0xfb, 0xb4, 0xfa, 0xb8, 0xf0, 0x7f, 0x59, 0x0, 0x0, 0x0, 0x7e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xca, 0x5, 0xb2, 0x2, 0x30, 0x18, 0x3, 0xe1, 0x4d, 0xed, 0xb9, 0x60, 0xf7, 0x3f, 0x20, 0xee, 0x4e, 0x99, 0xe0, 0xb0, 0x63, 0xfd, 0xbf, 0x86, 0xd7, 0x12, 0x72, 0x38, 0x69, 0x5b, 0x6b, 0x42, 0x45, 0xe5, 0xa, 0xab, 0x95, 0x41, 0x9f, 0x32, 0x20, 0x69, 0x2d, 0xbc, 0x50, 0x46, 0x3a, 0x10, 0x17, 0x5f, 0x49, 0x4, 0x7f, 0x90, 0x57, 0x89, 0xb7, 0xc5, 0x5f, 0x96, 0x17, 0x2e, 0x93, 0xcb, 0x8e, 0x3a, 0x83, 0xb, 0xc4, 0x8e, 0xd4, 0xff, 0x5c, 0x73, 0x83, 0x69, 0x9e, 0x95, 0xfc, 0x3b, 0xf4, 0x33, 0xe0, 0xf8, 0x61, 0xd3, 0xf1, 0x7d, 0x5d, 0x30, 0x7a, 0x6f, 0x89, 0xb, 0xd4, 0x5a, 0xe1, 0x40, 0xf, 0xfc, 0x34, 0x6c, 0xd2, 0x56, 0x80, 0xef, 0xfd, 0x9, 0xd2, 0x3a, 0x5e, 0x41, 0x15, 0x21, 0x77, 0x6, 0xc7, 0x6b, 0x47, 0x4e, 0x3a, 0x2f, 0x53, 0xb4, 0x10, 0xc7, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char checker_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x1a, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc4, 0x0, 0x5, 0x98, 0xc, 0xc6, 0x7a, 0xc2, 0x6a, 0x0, 0x8b, 0x7a, 0x2, 0x8d, 0x68, 0x67, 0xe3, 0xa, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char close_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9b, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x31, 0xe, 0xc2, 0x30, 0x10, 0x4, 0x17, 0xaa, 0x3d, 0x67, 0xdb, 0x58, 0xd0, 0xd3, 0xf0, 0xa3, 0x7c, 0x36, 0x3c, 0x82, 0x48, 0x44, 0x22, 0x6f, 0xb1, 0x4d, 0x85, 0x14, 0x81, 0xf, 0x2c, 0x28, 0xe0, 0xda, 0xd5, 0x8c, 0x4e, 0x77, 0xb, 0xfc, 0xd5, 0x98, 0xd9, 0x20, 0x29, 0x7a, 0xb9, 0xa4, 0x68, 0x66, 0x83, 0xb, 0x93, 0xcc, 0x24, 0xa7, 0x9a, 0x44, 0x52, 0x24, 0x39, 0x91, 0xcc, 0x55, 0x89, 0xa4, 0xde, 0xcc, 0xce, 0x24, 0xb, 0xc9, 0x39, 0x84, 0xb0, 0xf7, 0xb2, 0xae, 0xeb, 0x76, 0xde, 0x8a, 0x4f, 0x92, 0x66, 0xd8, 0x91, 0x5c, 0x49, 0x5e, 0x9a, 0xe1, 0xb5, 0x64, 0x5, 0x16, 0x92, 0x8b, 0x7, 0x6f, 0x9b, 0x8c, 0x0, 0x4a, 0x29, 0x9b, 0x26, 0x81, 0xa4, 0x3e, 0xa5, 0x34, 0x2, 0x38, 0x2, 0x58, 0x0, 0xcc, 0x0, 0xe, 0x39, 0xe7, 0xd3, 0xfa, 0xb0, 0xee, 0xea, 0x8f, 0x7, 0x7b, 0xf5, 0x9d, 0xb7, 0xb0, 0x97, 0x55, 0x25, 0x5f, 0x17, 0xe9, 0x2e, 0xf9, 0xb8, 0xca, 0x3f, 0x9b, 0x1b, 0x1a, 0xe3, 0x40, 0x47, 0xa0, 0xda, 0xda, 0x61, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char close_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9b, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x31, 0xe, 0xc2, 0x30, 0x10, 0x4, 0x17, 0xaa, 0x3d, 0x67, 0xdb, 0x58, 0xd0, 0xd3, 0xf0, 0xa3, 0x7c, 0x36, 0x3c, 0x82, 0x48, 0x44, 0x22, 0x6f, 0xb1, 0x4d, 0x85, 0x14, 0x81, 0xf, 0x2c, 0x28, 0xe0, 0xda, 0xd5, 0x8c, 0x4e, 0x77, 0xb, 0xfc, 0xd5, 0x98, 0xd9, 0x20, 0x29, 0x7a, 0xb9, 0xa4, 0x68, 0x66, 0x83, 0xb, 0x93, 0xcc, 0x24, 0xa7, 0x9a, 0x44, 0x52, 0x24, 0x39, 0x91, 0xcc, 0x55, 0x89, 0xa4, 0xde, 0xcc, 0xce, 0x24, 0xb, 0xc9, 0x39, 0x84, 0xb0, 0xf7, 0xb2, 0xae, 0xeb, 0x76, 0xde, 0x8a, 0x4f, 0x92, 0x66, 0xd8, 0x91, 0x5c, 0x49, 0x5e, 0x9a, 0xe1, 0xb5, 0x64, 0x5, 0x16, 0x92, 0x8b, 0x7, 0x6f, 0x9b, 0x8c, 0x0, 0x4a, 0x29, 0x9b, 0x26, 0x81, 0xa4, 0x3e, 0xa5, 0x34, 0x2, 0x38, 0x2, 0x58, 0x0, 0xcc, 0x0, 0xe, 0x39, 0xe7, 0xd3, 0xfa, 0xb0, 0xee, 0xea, 0x8f, 0x7, 0x7b, 0xf5, 0x9d, 0xb7, 0xb0, 0x97, 0x55, 0x25, 0x5f, 0x17, 0xe9, 0x2e, 0xf9, 0xb8, 0xca, 0x3f, 0x9b, 0x1b, 0x1a, 0xe3, 0x40, 0x47, 0xa0, 0xda, 0xda, 0x61, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char color_picker_hue_png[] = { @@ -59,31 +59,31 @@ static const unsigned char color_picker_hue_png[] = { }; static const unsigned char color_picker_sample_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x14, 0x8, 0x2, 0x0, 0x0, 0x0, 0xed, 0x20, 0x74, 0x8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x9, 0x18, 0xc, 0x27, 0x37, 0x29, 0x4f, 0x42, 0x2d, 0x0, 0x0, 0x0, 0x61, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xd9, 0xb1, 0xd, 0x0, 0x21, 0xc, 0x3, 0x40, 0x40, 0xbf, 0x5f, 0x66, 0xcd, 0x84, 0xf9, 0x96, 0x19, 0xf0, 0x5d, 0x87, 0x5c, 0x5b, 0x9, 0xca, 0x9e, 0x99, 0x75, 0xe9, 0xee, 0xfb, 0x59, 0x55, 0x52, 0xe9, 0xc3, 0xe9, 0x59, 0x10, 0x4c, 0x1, 0x50, 0x0, 0x50, 0x0, 0x8, 0xf4, 0xf9, 0x15, 0x49, 0x93, 0x53, 0x13, 0x0, 0x2b, 0x10, 0x28, 0x0, 0x28, 0x0, 0x64, 0xd9, 0x2e, 0xc1, 0xd2, 0xe4, 0xd4, 0x4, 0xc0, 0xa, 0x4, 0xa, 0x0, 0xa, 0x0, 0x59, 0x5c, 0x82, 0xa5, 0xd1, 0xa9, 0x9, 0x80, 0x15, 0x8, 0x14, 0x0, 0x14, 0x0, 0xb2, 0xfc, 0x5b, 0xb2, 0x3c, 0x5a, 0x4, 0xa1, 0xf3, 0x57, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x47, 0x29, 0xbc, 0x83, 0x0, 0x0, 0x0, 0x3c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd5, 0x21, 0x11, 0x0, 0x30, 0xc, 0x4, 0xc1, 0xfa, 0x57, 0x53, 0x87, 0xed, 0x4, 0x45, 0xc4, 0xed, 0xa3, 0xc3, 0x4b, 0xfe, 0xbc, 0xd9, 0x9d, 0x35, 0x2b, 0xe, 0x0, 0x0, 0x0, 0x80, 0xed, 0x66, 0xc5, 0x1, 0x0, 0x0, 0x0, 0xe0, 0x6, 0x1, 0x0, 0x0, 0x90, 0x6, 0x70, 0x83, 0x0, 0x0, 0x0, 0x28, 0x3, 0x7c, 0x54, 0x93, 0xd6, 0xf1, 0xd1, 0x16, 0x8a, 0x17, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char dosfont_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0xeb, 0x45, 0x5c, 0x66, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x1, 0xdd, 0x8a, 0x13, 0xa4, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x89, 0x0, 0x0, 0xb, 0x89, 0x1, 0x37, 0xc9, 0xcb, 0xad, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x2, 0x83, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0xd4, 0xb1, 0x6e, 0xdb, 0x30, 0x10, 0x0, 0x50, 0x22, 0x3, 0x27, 0x22, 0xc8, 0x78, 0x83, 0x91, 0xa9, 0x1f, 0xc0, 0xa9, 0x10, 0xa, 0x7e, 0xc, 0x11, 0x14, 0x87, 0xc, 0x1c, 0x32, 0x9, 0x1a, 0xe, 0x46, 0xa6, 0xfc, 0x43, 0xff, 0x86, 0xb5, 0x80, 0x9b, 0x88, 0x8e, 0x5d, 0x64, 0x18, 0x9e, 0xdc, 0xd5, 0x53, 0x91, 0xc1, 0xa0, 0x7a, 0xa4, 0xe4, 0xd4, 0x31, 0xd2, 0x25, 0x70, 0x97, 0xa2, 0x37, 0x48, 0xe6, 0x33, 0x45, 0xdd, 0x51, 0x24, 0x95, 0x7a, 0x23, 0xe0, 0x75, 0x13, 0xb, 0xd8, 0x93, 0xbf, 0x51, 0x51, 0xa3, 0xac, 0x99, 0xc9, 0x29, 0x87, 0x81, 0x83, 0xb2, 0x0, 0xc7, 0xfe, 0xee, 0x43, 0x58, 0x85, 0x95, 0xb7, 0xa6, 0xb6, 0xaf, 0x7a, 0xe9, 0x93, 0x63, 0xc3, 0xf2, 0xc, 0x96, 0x3e, 0xba, 0x97, 0x11, 0x3, 0xb5, 0x46, 0xc0, 0x15, 0x30, 0x43, 0x1, 0xbd, 0x6, 0x81, 0x71, 0xa9, 0xb2, 0x82, 0x9, 0x92, 0x3d, 0xf6, 0xb0, 0xbd, 0x5c, 0xf2, 0x53, 0xf2, 0x75, 0xc, 0x11, 0x5f, 0xc7, 0xe0, 0xc4, 0xaa, 0xb4, 0x40, 0x41, 0xc4, 0x69, 0xd0, 0x27, 0x55, 0x12, 0x13, 0x78, 0x74, 0xa7, 0xb5, 0xd8, 0x3f, 0x14, 0x77, 0x81, 0x0, 0x22, 0x93, 0x9b, 0x4c, 0x54, 0x5b, 0x72, 0x6d, 0x98, 0x17, 0xd1, 0x33, 0xb3, 0x94, 0xaa, 0x3c, 0x93, 0xea, 0xb4, 0x76, 0x31, 0x6a, 0x66, 0x0, 0xa9, 0x59, 0x1c, 0x8c, 0xe, 0x33, 0xc0, 0x12, 0x4c, 0x29, 0xc2, 0xa5, 0xc3, 0xc1, 0xd0, 0xb2, 0x64, 0x6a, 0x60, 0xa3, 0xc0, 0xea, 0xac, 0x19, 0x58, 0x4b, 0xa9, 0x4a, 0x17, 0xf0, 0xda, 0x68, 0xb6, 0x5, 0xec, 0xb2, 0xf6, 0x88, 0x33, 0xc8, 0x18, 0x52, 0xd5, 0x5a, 0x1, 0xb3, 0x61, 0x1, 0x53, 0xdf, 0x2, 0x51, 0x2d, 0x33, 0xdd, 0x12, 0x59, 0xea, 0x94, 0x95, 0x3c, 0x80, 0x2e, 0x5e, 0xf9, 0xdb, 0x71, 0x73, 0x70, 0xcf, 0x39, 0x3b, 0x76, 0xb7, 0xbb, 0x7d, 0xcf, 0x74, 0x50, 0xd, 0x62, 0x40, 0x44, 0x6, 0x83, 0xfe, 0xc7, 0x8e, 0x51, 0x5, 0x5c, 0xe1, 0xdd, 0xdd, 0xaa, 0xc2, 0xf8, 0x53, 0x80, 0x31, 0xe2, 0xfd, 0x7d, 0x14, 0x8, 0x7e, 0xcc, 0x5, 0x28, 0x62, 0xd7, 0xc5, 0xc, 0xa6, 0xf3, 0xc3, 0x46, 0xa6, 0x30, 0xd7, 0x1e, 0x1b, 0x2e, 0xd0, 0x7f, 0x63, 0x5f, 0x1f, 0xf1, 0x32, 0xc9, 0x90, 0x82, 0xef, 0xb9, 0x82, 0xc, 0x5a, 0x1, 0xef, 0x66, 0x90, 0xd7, 0x7a, 0x2c, 0x80, 0x13, 0x94, 0xc4, 0xf6, 0x9f, 0xd8, 0x75, 0xbb, 0x2c, 0x89, 0xed, 0xff, 0x42, 0xe9, 0xa7, 0xfb, 0xa4, 0x84, 0xec, 0x13, 0x45, 0x7, 0x1a, 0xb9, 0x97, 0x18, 0x65, 0xab, 0x4, 0xf9, 0x54, 0x8c, 0x3c, 0x54, 0xe8, 0xed, 0xa3, 0x7c, 0x7b, 0x55, 0xe0, 0x8b, 0x0, 0xf6, 0x4f, 0x36, 0xd6, 0x3d, 0xc3, 0xe8, 0x41, 0xc0, 0xa1, 0xb1, 0xdb, 0x9, 0xa8, 0x29, 0x0, 0xe, 0xec, 0xc3, 0x4, 0xc1, 0x8, 0xc, 0x0, 0xd6, 0x36, 0xf3, 0x23, 0xba, 0x80, 0x3, 0x6f, 0x17, 0x15, 0xbe, 0x4b, 0xe5, 0x8c, 0x23, 0xc2, 0x57, 0x7b, 0x5d, 0x61, 0x53, 0xc0, 0x61, 0xf, 0xbd, 0xd5, 0x15, 0x68, 0x47, 0x8e, 0x0, 0x7b, 0x37, 0xba, 0xab, 0xba, 0x59, 0xcc, 0x79, 0x3d, 0xd7, 0xaf, 0x5a, 0xe3, 0x85, 0x66, 0x69, 0xab, 0x16, 0x32, 0x31, 0x5b, 0xd0, 0xdb, 0x66, 0x2, 0x2f, 0x6f, 0xe, 0xb2, 0x42, 0xb5, 0x87, 0xdf, 0xf0, 0x59, 0xae, 0x6a, 0x86, 0xae, 0x93, 0x4c, 0x7d, 0x1b, 0x9a, 0x6b, 0x84, 0xdd, 0x9a, 0xd6, 0xca, 0xc8, 0x89, 0xc3, 0xfb, 0xd4, 0x82, 0xe, 0x30, 0xa2, 0x2c, 0x18, 0xc2, 0x98, 0xb2, 0x4f, 0x8, 0xba, 0x83, 0xa1, 0x40, 0x12, 0x88, 0x6f, 0x43, 0x38, 0x82, 0x1c, 0x1f, 0x15, 0x70, 0x82, 0x96, 0xa8, 0xa5, 0x3d, 0xed, 0x9c, 0xde, 0xb9, 0x1, 0xe9, 0xb8, 0x5f, 0x4c, 0x39, 0xd2, 0xa6, 0xac, 0xa6, 0x48, 0xe7, 0xd0, 0x95, 0x53, 0xb0, 0xc4, 0x7b, 0x97, 0xd4, 0xcd, 0x3c, 0xdd, 0xf0, 0xd0, 0x4e, 0xbf, 0xe6, 0x65, 0x24, 0x5b, 0x7b, 0x7d, 0xe, 0xe5, 0xd6, 0xae, 0xe9, 0x90, 0x64, 0xfd, 0x70, 0x9e, 0x21, 0xb5, 0x6c, 0x5, 0xa4, 0xa2, 0x87, 0xe9, 0xa3, 0x25, 0xf4, 0x5, 0x5c, 0x39, 0x61, 0xa6, 0x1e, 0xbe, 0x11, 0x18, 0x80, 0xed, 0xb, 0x18, 0x9b, 0x70, 0x70, 0xec, 0x5f, 0x80, 0x3f, 0x26, 0x27, 0xb3, 0xc9, 0x33, 0xc8, 0x5c, 0x2c, 0x5a, 0x59, 0x1f, 0xcb, 0x2c, 0x89, 0x9d, 0xae, 0xf, 0x7d, 0xcc, 0xdb, 0x9c, 0xdd, 0xd5, 0xed, 0x7c, 0x7f, 0xbe, 0xd0, 0x52, 0xf9, 0x1f, 0xff, 0x76, 0xfc, 0x2, 0x24, 0x3a, 0x65, 0x42, 0xf6, 0x41, 0x91, 0x95, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0xeb, 0x45, 0x5c, 0x66, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x2, 0x64, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xbc, 0xd4, 0x81, 0x86, 0x2c, 0x47, 0x14, 0xc6, 0xf1, 0xcf, 0x45, 0x81, 0x32, 0x2, 0x38, 0x58, 0x17, 0xe4, 0x1, 0xa, 0x44, 0x8b, 0x7a, 0x98, 0xb2, 0xe2, 0xb8, 0x28, 0x2c, 0x68, 0x8d, 0x63, 0x5c, 0xb0, 0xef, 0x90, 0xb7, 0xe9, 0x6c, 0x71, 0x40, 0x9, 0x20, 0xd0, 0x63, 0x2d, 0x98, 0x0, 0xc, 0x88, 0x60, 0x54, 0xa7, 0xaa, 0xba, 0xef, 0x66, 0x67, 0x5d, 0x10, 0x37, 0xf7, 0xf, 0xc3, 0x4f, 0x39, 0x53, 0x87, 0xd2, 0xf8, 0x5a, 0x84, 0x9b, 0xb8, 0x81, 0xc3, 0x6b, 0xc4, 0x90, 0x1, 0xce, 0xee, 0xe4, 0xe1, 0x39, 0x6a, 0x84, 0x23, 0xda, 0x80, 0xe1, 0x7f, 0x8c, 0x4f, 0xf1, 0x29, 0x38, 0x8b, 0xd6, 0x87, 0x4, 0x8f, 0x32, 0xf, 0xa, 0x67, 0x99, 0x2a, 0x98, 0x4, 0x42, 0x94, 0xd1, 0x56, 0xf0, 0xd, 0xec, 0xd2, 0xc0, 0x9c, 0xa8, 0xc2, 0x7a, 0x44, 0x1, 0x6d, 0x90, 0xdd, 0x97, 0x13, 0x2e, 0x1, 0x28, 0x8f, 0x39, 0xf4, 0x19, 0x55, 0x42, 0x9f, 0xa1, 0x59, 0x41, 0x4, 0x10, 0x68, 0xe6, 0x6d, 0xe8, 0x23, 0xac, 0xeb, 0xf0, 0xd9, 0xbf, 0xdd, 0xc5, 0xdd, 0x2c, 0xf7, 0x4d, 0x23, 0x11, 0x5b, 0x86, 0x22, 0x82, 0x96, 0x0, 0x83, 0xea, 0xdd, 0x1c, 0x54, 0x15, 0x30, 0x8, 0x2a, 0x98, 0x8c, 0xf1, 0xf3, 0x6c, 0x54, 0x89, 0x2c, 0x5c, 0x75, 0xb2, 0x26, 0xee, 0x40, 0x47, 0xb2, 0x15, 0xc8, 0xe7, 0xeb, 0xd5, 0xca, 0x11, 0x70, 0xb0, 0xf4, 0xc, 0x72, 0xa6, 0x18, 0x25, 0x35, 0x40, 0x80, 0x69, 0x10, 0x8c, 0x35, 0xea, 0x1a, 0xb8, 0xa3, 0x6d, 0x30, 0xef, 0x40, 0x44, 0x20, 0x9c, 0x40, 0xaa, 0x56, 0x2b, 0xd8, 0xfe, 0x2f, 0x34, 0xe3, 0x58, 0xe4, 0xa3, 0x88, 0x93, 0x9, 0xce, 0x20, 0x90, 0xe0, 0xfb, 0xf4, 0xc3, 0xd5, 0xff, 0x5d, 0x8a, 0x57, 0xff, 0xf1, 0x7c, 0x49, 0x2a, 0x57, 0xc, 0xcc, 0x91, 0x99, 0x95, 0x2c, 0x87, 0x3f, 0xcf, 0xca, 0x88, 0xfc, 0xc4, 0xf7, 0xf7, 0x4f, 0x1d, 0xd6, 0xbf, 0x2a, 0x28, 0xcf, 0xfc, 0xe9, 0xd3, 0x5c, 0x21, 0x86, 0xb5, 0x34, 0x90, 0x99, 0xa7, 0x69, 0x2e, 0x64, 0xa7, 0xb0, 0x3c, 0xab, 0xa0, 0xf4, 0x13, 0xcf, 0xda, 0x20, 0xfd, 0xae, 0x1, 0x5a, 0x21, 0x4, 0x55, 0xca, 0x31, 0x24, 0x6d, 0xd0, 0x86, 0x76, 0xe0, 0xfb, 0x1d, 0x38, 0x72, 0xe0, 0x6, 0xbc, 0x41, 0xbb, 0xd8, 0xe5, 0x67, 0xf5, 0xd3, 0xb9, 0x24, 0x95, 0xcb, 0xff, 0xb0, 0x3a, 0xdc, 0x2d, 0xc, 0x15, 0xe4, 0x2a, 0xab, 0xa6, 0xda, 0xea, 0xe1, 0x23, 0x8, 0xca, 0xba, 0x74, 0x48, 0xee, 0xb3, 0x55, 0xa0, 0xc1, 0xaf, 0x15, 0x38, 0x3d, 0xba, 0xd9, 0xa2, 0x43, 0xa0, 0xa, 0x9e, 0xad, 0x7b, 0xd9, 0x40, 0x86, 0x6, 0xe4, 0xc9, 0x3d, 0x6c, 0x10, 0x6d, 0x85, 0x85, 0xc8, 0xb9, 0x61, 0x3, 0x36, 0xd, 0x3c, 0x5, 0x77, 0xd7, 0xe1, 0xf, 0x56, 0x52, 0x5e, 0x99, 0x7e, 0x73, 0x87, 0xe, 0xcf, 0xd, 0x3c, 0x27, 0x4a, 0xce, 0x74, 0x90, 0xb3, 0x78, 0x21, 0x4e, 0x7e, 0xf5, 0x1f, 0x7c, 0x83, 0xaa, 0xb7, 0x1d, 0xf0, 0xb6, 0x15, 0xdf, 0xa6, 0x17, 0xdc, 0x61, 0xc0, 0xb, 0x99, 0x97, 0x61, 0x83, 0x0, 0x8b, 0x88, 0x40, 0x26, 0xd0, 0xbf, 0xf0, 0xb, 0x2, 0xb0, 0xc3, 0x34, 0x89, 0x97, 0x30, 0xc6, 0xe1, 0xc0, 0x74, 0x3e, 0xc9, 0x9, 0x36, 0x6a, 0xd4, 0x4b, 0x1e, 0xc9, 0x44, 0x5a, 0x59, 0x19, 0xc2, 0x73, 0x2e, 0x21, 0x33, 0x99, 0x89, 0x96, 0x6, 0xb9, 0xc2, 0xfc, 0x75, 0x88, 0x5f, 0x40, 0xb3, 0x76, 0xe0, 0xd, 0x46, 0x91, 0x51, 0x2e, 0x72, 0xf6, 0xe6, 0xec, 0x17, 0x16, 0xc1, 0x96, 0x5, 0x18, 0xad, 0xb0, 0x43, 0x7e, 0xf, 0x13, 0xe0, 0xd1, 0xba, 0xfc, 0xe7, 0x77, 0xd3, 0x3b, 0xd0, 0xc3, 0x78, 0xf3, 0xb9, 0x22, 0xa2, 0xd3, 0x7b, 0x40, 0x6d, 0x3c, 0xc9, 0x35, 0xa7, 0x94, 0xb4, 0xec, 0x90, 0x47, 0x75, 0x15, 0x58, 0xf3, 0x3, 0xe, 0x1d, 0x38, 0x34, 0xf0, 0x4a, 0xb5, 0xe, 0x61, 0xa8, 0xb0, 0x90, 0xba, 0x57, 0xb0, 0x2e, 0xf3, 0xe2, 0x35, 0xbc, 0x82, 0xfe, 0x94, 0xfd, 0xca, 0xaa, 0x3b, 0xc8, 0x45, 0xee, 0x46, 0xe2, 0x74, 0x2c, 0xf5, 0x62, 0x6f, 0xdf, 0x87, 0xc1, 0x9e, 0x7d, 0xf7, 0xfb, 0xcf, 0x18, 0xe4, 0xa0, 0xf4, 0xf, 0x22, 0x3c, 0x3d, 0xa, 0x46, 0x1, 0x0, 0x24, 0x3a, 0x65, 0x42, 0x42, 0xc7, 0x4f, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char dropdown_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x5, 0x1f, 0x98, 0x19, 0x18, 0x18, 0x3f, 0x30, 0xff, 0xd3, 0x83, 0x70, 0xff, 0x33, 0x33, 0x30, 0x8, 0x9f, 0x61, 0xf9, 0x6b, 0xff, 0x65, 0x2f, 0x3, 0x1c, 0xbc, 0xea, 0x66, 0x62, 0xbc, 0xcf, 0xc0, 0xc0, 0xf0, 0x7, 0x26, 0xc0, 0x74, 0x89, 0x89, 0xe9, 0x29, 0x9f, 0x14, 0x3, 0xb, 0xc3, 0x5f, 0x6, 0x6, 0x6, 0x6, 0xee, 0x38, 0x91, 0x25, 0xc, 0xc, 0xc, 0x1f, 0xd8, 0xde, 0x4b, 0x3e, 0xfc, 0xff, 0xf0, 0xff, 0x9b, 0x58, 0xb8, 0xce, 0xf, 0x6c, 0xef, 0xe4, 0xde, 0xa4, 0x32, 0x20, 0x83, 0xf, 0x4c, 0x30, 0x16, 0x0, 0x75, 0xad, 0x1b, 0x7f, 0x65, 0xec, 0x78, 0x4c, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0xf8, 0xc0, 0xcc, 0x0, 0x2, 0x60, 0x16, 0x98, 0x78, 0x67, 0x8, 0x81, 0x6f, 0x4d, 0xde, 0x9a, 0x0, 0x5, 0xde, 0x3a, 0x3d, 0xfc, 0x8f, 0x80, 0xaf, 0xba, 0x18, 0xde, 0x29, 0x2, 0x19, 0xbf, 0x61, 0x2, 0x6f, 0x62, 0x18, 0x3e, 0xb0, 0xbd, 0x97, 0x4, 0x32, 0xff, 0x80, 0xb9, 0xb1, 0x20, 0x93, 0xc0, 0x42, 0x8, 0x2e, 0x54, 0xe8, 0x9d, 0xdc, 0x9b, 0x54, 0x10, 0xb, 0x21, 0xc4, 0x4, 0x63, 0x1, 0x0, 0x86, 0x1f, 0x3b, 0x1e, 0x92, 0x22, 0x3f, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char error_icon_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x4, 0x0, 0x0, 0x0, 0xd9, 0x73, 0xb2, 0x7f, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x5d, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x94, 0xc1, 0xe, 0x80, 0x20, 0xc, 0x43, 0x5b, 0xe3, 0xff, 0xff, 0x72, 0x3d, 0xcc, 0x85, 0xa1, 0xbb, 0x18, 0xf4, 0x62, 0xca, 0xa9, 0x74, 0xe3, 0x65, 0xd, 0x1, 0xa, 0x6b, 0x6b, 0x5b, 0x3c, 0x6f, 0x80, 0x1, 0x7f, 0x1, 0xec, 0x29, 0x78, 0xbe, 0x2a, 0x31, 0x94, 0x98, 0x9e, 0x98, 0xf5, 0xab, 0x37, 0x1, 0xaa, 0x19, 0x8d, 0xe2, 0x50, 0xb1, 0x9b, 0xbd, 0xb7, 0x23, 0x8c, 0x21, 0x7b, 0xd5, 0xf5, 0x3d, 0x88, 0x50, 0x1, 0xdf, 0x46, 0xb8, 0xf, 0x5c, 0xef, 0xa6, 0xab, 0xd1, 0x7f, 0xa2, 0x1, 0x6, 0x0, 0x0, 0xe, 0x62, 0x6, 0x31, 0x47, 0xb6, 0x7f, 0xdd, 0x14, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x4, 0x0, 0x0, 0x0, 0xd9, 0x73, 0xb2, 0x7f, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x36, 0x18, 0x5, 0xa3, 0x60, 0x14, 0xfc, 0x87, 0x40, 0x38, 0xb, 0x21, 0x6, 0x6, 0x18, 0x62, 0x98, 0x6, 0xa0, 0xb1, 0xfe, 0xe3, 0x67, 0xd1, 0xc2, 0x0, 0x10, 0xc4, 0xc5, 0x82, 0x91, 0x43, 0xc0, 0xb, 0xb8, 0x15, 0x63, 0x78, 0x6, 0x5, 0x8c, 0x82, 0x51, 0x30, 0xa, 0x0, 0x35, 0xa3, 0x4c, 0xb4, 0x7c, 0x8a, 0x7, 0x6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xb9, 0xa2, 0x9b, 0xc9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xb9, 0xa2, 0x9b, 0xc9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char frame_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xcc, 0x40, 0x27, 0xb9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x4, 0x3, 0x0, 0x0, 0x0, 0xa4, 0x5b, 0x41, 0xd4, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0x47, 0x8c, 0xbf, 0xff, 0xff, 0xff, 0xcc, 0x40, 0x27, 0xb9, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe, 0x39, 0x68, 0x7a, 0x7b, 0x3a, 0x74, 0x10, 0x8, 0x69, 0xf, 0x6, 0x75, 0x11, 0xb8, 0x16, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x10, 0x32, 0x9, 0xd, 0x75, 0x56, 0x64, 0x48, 0xef, 0x9c, 0x39, 0x73, 0x46, 0x19, 0xc3, 0x6a, 0x6, 0x20, 0xd8, 0xc5, 0x10, 0x3, 0xa2, 0x8e, 0x32, 0x44, 0x82, 0xa8, 0xa9, 0xd8, 0x29, 0xa8, 0x12, 0xb0, 0x6, 0x29, 0x86, 0xdc, 0x9d, 0x33, 0x67, 0xce, 0x2b, 0x63, 0x10, 0x3, 0x1b, 0x6, 0x0, 0xdf, 0xc6, 0x11, 0x6d, 0xb8, 0xf4, 0x9c, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char full_panel_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x44, 0xa4, 0x8a, 0xc6, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0x26, 0x26, 0x28, 0x25, 0x25, 0x27, 0x24, 0x24, 0x26, 0x23, 0x23, 0x25, 0x22, 0x22, 0x24, 0x21, 0x21, 0x23, 0x1e, 0x1e, 0x20, 0x1d, 0x1d, 0x1f, 0x1c, 0x1c, 0x1e, 0x31, 0x30, 0x32, 0x50, 0x4e, 0x54, 0x4e, 0x4c, 0x50, 0x4c, 0x4a, 0x4e, 0x3d, 0x3b, 0x3f, 0x38, 0x36, 0x3a, 0xff, 0xff, 0xff, 0x4, 0xb3, 0x69, 0x9b, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x10, 0x95, 0xb2, 0xd, 0x2c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0xed, 0x93, 0x29, 0x12, 0xc0, 0x30, 0xc, 0xc4, 0x7c, 0x3b, 0x67, 0xf3, 0xff, 0xdf, 0xb6, 0xe1, 0xce, 0x98, 0x15, 0x45, 0x58, 0xb3, 0x68, 0x5, 0x0, 0x80, 0x48, 0x21, 0x88, 0xb0, 0x41, 0x62, 0x51, 0xb, 0x50, 0x61, 0xda, 0xa, 0xb1, 0x79, 0xa9, 0x1, 0xc5, 0x8d, 0xe9, 0x1b, 0x60, 0x6b, 0x7d, 0xcc, 0x80, 0xd1, 0x9b, 0x31, 0x2, 0x8a, 0xf7, 0x67, 0x85, 0x3c, 0xdd, 0x5, 0x81, 0xb4, 0x8c, 0x75, 0x60, 0x14, 0x25, 0x20, 0xab, 0xf3, 0x24, 0xcc, 0x6a, 0x57, 0xb8, 0xc2, 0xff, 0x42, 0x76, 0xda, 0xf4, 0xf6, 0x69, 0x38, 0x69, 0x7a, 0x79, 0xbc, 0x49, 0xfe, 0x2f, 0x65, 0xd3, 0x2d, 0x45, 0xb, 0x5e, 0xbc, 0x3b, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x4, 0x3, 0x0, 0x0, 0x0, 0x81, 0x54, 0x67, 0xc7, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0x26, 0x26, 0x28, 0x25, 0x25, 0x27, 0x24, 0x24, 0x26, 0x23, 0x23, 0x25, 0x22, 0x22, 0x24, 0x21, 0x21, 0x23, 0x1e, 0x1e, 0x20, 0x1d, 0x1d, 0x1f, 0x1c, 0x1c, 0x1e, 0x31, 0x30, 0x32, 0x50, 0x4e, 0x54, 0x4e, 0x4c, 0x50, 0x4c, 0x4a, 0x4e, 0x3d, 0x3b, 0x3f, 0x38, 0x36, 0x3a, 0xb3, 0xde, 0x6f, 0x4d, 0x0, 0x0, 0x0, 0x5a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x60, 0x14, 0x52, 0x82, 0x3, 0x45, 0x1, 0x20, 0x5f, 0xd9, 0x35, 0xd, 0xe, 0x42, 0x8c, 0x4, 0x18, 0x98, 0xcc, 0x2a, 0x66, 0xc2, 0x41, 0x7b, 0xb2, 0x2, 0x83, 0x70, 0xd6, 0x9e, 0xbb, 0x70, 0x70, 0x7a, 0x99, 0x21, 0x83, 0x48, 0xf5, 0xfb, 0xff, 0x70, 0xf0, 0x6f, 0xbb, 0x23, 0x83, 0x6a, 0xcf, 0x7f, 0x24, 0x70, 0x22, 0x88, 0x41, 0x6d, 0x2e, 0xb2, 0xc0, 0xcd, 0x24, 0x8a, 0x5, 0x46, 0x5, 0x30, 0x2, 0x19, 0x23, 0x1a, 0x30, 0x22, 0xa, 0x23, 0x2a, 0x31, 0x22, 0x1b, 0x23, 0x39, 0x0, 0x0, 0x8c, 0xb1, 0x80, 0xd2, 0x41, 0x59, 0x8c, 0x74, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_png[] = { @@ -91,391 +91,395 @@ static const unsigned char graph_node_png[] = { }; static const unsigned char graph_node_breakpoint_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x6, 0xf, 0x3b, 0x1c, 0xec, 0x64, 0x51, 0x75, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x8f, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0xd7, 0xbd, 0x9, 0xc0, 0x20, 0x10, 0x5, 0xe0, 0x53, 0x2c, 0x5d, 0x40, 0x74, 0x4, 0xf7, 0x9f, 0xc4, 0x11, 0x22, 0x2e, 0x60, 0x6f, 0x9a, 0x13, 0x4e, 0x21, 0x41, 0x50, 0x48, 0x91, 0x77, 0x95, 0xf8, 0xf3, 0x79, 0x62, 0xf5, 0x88, 0x36, 0x4b, 0xf5, 0x41, 0x2d, 0xf1, 0x22, 0x22, 0xbf, 0x78, 0x2e, 0x5b, 0x97, 0x2, 0xc9, 0xc3, 0xc, 0x2c, 0x95, 0xdc, 0x6f, 0x78, 0xce, 0x5b, 0x97, 0xd4, 0x42, 0x27, 0xd9, 0xba, 0x14, 0xac, 0x4b, 0xa1, 0x96, 0xd8, 0x24, 0x20, 0x9f, 0x41, 0x1d, 0x7b, 0xba, 0x59, 0xb6, 0xaf, 0xa7, 0x3d, 0x7e, 0x78, 0xdb, 0x54, 0xbc, 0x36, 0x74, 0xa7, 0x77, 0x7f, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e, 0x4, 0xe4, 0xb7, 0xfc, 0xc8, 0x6b, 0x59, 0xce, 0x99, 0x39, 0x95, 0x71, 0xb4, 0x6b, 0x4b, 0x89, 0xf5, 0x44, 0x72, 0x3d, 0x93, 0x9d, 0x3f, 0xad, 0x1b, 0x54, 0xed, 0x49, 0xd3, 0x36, 0x45, 0x4f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0xe7, 0x2c, 0xf4, 0xe7, 0x2c, 0xec, 0x5a, 0x6b, 0x42, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_close_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x56, 0x75, 0x5c, 0xe7, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x93, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0x95, 0x92, 0x31, 0xe, 0xc2, 0x30, 0x10, 0x4, 0x17, 0xaa, 0x3d, 0x7b, 0xdb, 0x58, 0xa4, 0xa7, 0xe1, 0x47, 0xfe, 0x2c, 0x3c, 0x82, 0x48, 0x44, 0x22, 0x6f, 0xb1, 0x4d, 0x15, 0xc9, 0x20, 0x1f, 0x52, 0xae, 0xdd, 0xd9, 0x2b, 0xe6, 0xe, 0x0, 0x60, 0x66, 0x59, 0x52, 0x82, 0x33, 0x92, 0x92, 0x99, 0x65, 0xec, 0x30, 0xc9, 0x4a, 0x72, 0x19, 0x95, 0x24, 0x25, 0x92, 0xb, 0xc9, 0x6a, 0x66, 0x19, 0x92, 0x26, 0x33, 0x7b, 0x92, 0x6c, 0x24, 0xd7, 0x10, 0xc2, 0xdc, 0xc1, 0x5f, 0x59, 0x8c, 0xf1, 0x32, 0xc, 0x42, 0x8, 0xb3, 0xb, 0x3b, 0xdb, 0xde, 0x24, 0x5f, 0x2e, 0xdc, 0x97, 0x3a, 0xb0, 0x91, 0xdc, 0x7e, 0xe1, 0xb3, 0x67, 0x66, 0x9f, 0xd6, 0xda, 0x69, 0x58, 0x90, 0x34, 0x95, 0x52, 0xee, 0x0, 0x6e, 0x0, 0x36, 0x0, 0x2b, 0x80, 0x6b, 0xad, 0xf5, 0xd1, 0x8b, 0x70, 0x6d, 0xb8, 0xf6, 0xfe, 0xd9, 0x18, 0x96, 0xe, 0x1f, 0xe, 0x38, 0xf6, 0x1a, 0x1f, 0x9f, 0xec, 0x40, 0x47, 0x56, 0x51, 0x84, 0x77, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x75, 0x90, 0xb5, 0x1, 0xc0, 0x30, 0x10, 0x3, 0x55, 0x1a, 0xbe, 0xc, 0x6e, 0xe6, 0xb5, 0xc3, 0x34, 0x4b, 0x98, 0xcc, 0xee, 0x7c, 0x4f, 0x92, 0x0, 0x70, 0x45, 0x19, 0x8c, 0x47, 0x19, 0x57, 0x37, 0x66, 0x1b, 0x6b, 0x74, 0x89, 0x32, 0xd6, 0xb0, 0xed, 0x2c, 0x51, 0xca, 0x6b, 0xb6, 0xb3, 0x41, 0x94, 0x0, 0xfe, 0x9f, 0x2c, 0x0, 0xa3, 0x64, 0x61, 0xa3, 0x6f, 0x66, 0xbd, 0xc6, 0x7f, 0xe9, 0x86, 0x3b, 0x5b, 0x34, 0x76, 0xa, 0xcf, 0xad, 0xe0, 0xaa, 0xbf, 0xa4, 0x4f, 0x5a, 0xa, 0x6d, 0x25, 0xba, 0x14, 0x37, 0x18, 0x8b, 0xe4, 0x0, 0x6f, 0xe9, 0x37, 0x83, 0x22, 0x73, 0x83, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_comment_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x19, 0x11, 0x2a, 0x1d, 0xd6, 0x78, 0x8b, 0x40, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x1, 0x74, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x97, 0xbf, 0x4e, 0xc2, 0x50, 0x14, 0xc6, 0x7f, 0x6d, 0x91, 0x56, 0x1b, 0x90, 0x80, 0x2c, 0x44, 0xc2, 0xa0, 0x2e, 0xe, 0x3e, 0x3, 0x89, 0x93, 0xf1, 0x1d, 0x4c, 0x18, 0x4d, 0x1c, 0x7c, 0xb, 0x57, 0x7, 0x13, 0x47, 0x13, 0x77, 0x46, 0xe3, 0xc4, 0x4b, 0x98, 0x60, 0x4c, 0xd4, 0x81, 0x60, 0x58, 0x94, 0xff, 0x22, 0x2d, 0xd0, 0xd6, 0xe5, 0xde, 0x88, 0x8, 0x2, 0x35, 0x6e, 0xf7, 0x5b, 0x6e, 0x9a, 0x9c, 0xef, 0x77, 0xee, 0x39, 0x37, 0x1d, 0x3e, 0x8d, 0x2f, 0x69, 0x80, 0xe, 0x18, 0xe2, 0xd4, 0xf8, 0xae, 0x0, 0xf0, 0x1, 0x4f, 0x9c, 0x1, 0x63, 0x45, 0x3a, 0xb0, 0x6, 0x24, 0x81, 0x34, 0x10, 0x7, 0x56, 0x26, 0x0, 0x43, 0xa0, 0x3, 0xbc, 0x2, 0xd, 0xe0, 0x3, 0xf0, 0x65, 0x57, 0x1b, 0xd8, 0x5e, 0x8f, 0x25, 0x8e, 0x2d, 0xd3, 0x3a, 0x88, 0x46, 0xcd, 0x4d, 0xa6, 0x68, 0x30, 0x70, 0x5f, 0x1c, 0xd7, 0xb9, 0x6d, 0x77, 0x5b, 0x97, 0xc0, 0x13, 0xd0, 0xd3, 0x44, 0xa7, 0x5c, 0x32, 0x91, 0x3a, 0xdf, 0xca, 0xed, 0x1c, 0x16, 0x52, 0xdd, 0x2a, 0xbf, 0xe8, 0xaa, 0x1e, 0xcb, 0x3e, 0x57, 0x1e, 0x6f, 0x1a, 0xad, 0xfa, 0x29, 0x50, 0x91, 0x33, 0xa7, 0x4d, 0xd3, 0xca, 0xcf, 0x33, 0x3, 0x14, 0x52, 0xdd, 0xaa, 0x69, 0x5a, 0x79, 0x31, 0xaa, 0x21, 0x97, 0x65, 0x47, 0x8c, 0x88, 0xcd, 0x82, 0x12, 0xb5, 0x36, 0xa0, 0x49, 0x80, 0xc1, 0xf2, 0x32, 0x24, 0x20, 0x90, 0x4f, 0x12, 0x46, 0x3a, 0x7f, 0x94, 0x2, 0x28, 0x80, 0x2, 0x88, 0x5f, 0x7b, 0xfc, 0xe3, 0xec, 0xe1, 0x3d, 0x1b, 0x1a, 0x30, 0x1a, 0x7a, 0x1c, 0x65, 0xd8, 0x5f, 0xc4, 0x74, 0x5d, 0xa3, 0xa4, 0x5e, 0x41, 0x1, 0x14, 0x40, 0x1, 0x14, 0x40, 0x1, 0x14, 0x40, 0x1, 0xfe, 0x15, 0xa0, 0x4d, 0xc9, 0x88, 0x4b, 0xdf, 0xc0, 0xf, 0xe1, 0xf5, 0x25, 0xc0, 0x7, 0x1c, 0xcf, 0xf3, 0xdc, 0x45, 0x9d, 0xa2, 0xd6, 0x1, 0x7c, 0x5d, 0x44, 0xd9, 0xba, 0xe3, 0xf6, 0xcb, 0xc5, 0xa6, 0x5d, 0x9a, 0x67, 0x2e, 0x36, 0xed, 0x92, 0xe3, 0xf6, 0xcb, 0x40, 0x1d, 0xf0, 0x64, 0x72, 0x5d, 0x7, 0xf6, 0xe2, 0x76, 0xe2, 0xc2, 0x32, 0x57, 0x77, 0xd, 0xc3, 0xd0, 0x67, 0x74, 0xf6, 0x1d, 0xb7, 0x7f, 0xdf, 0xe9, 0xb5, 0x4e, 0x80, 0x3b, 0xa0, 0xad, 0x8d, 0x45, 0xb8, 0x38, 0x90, 0x1, 0x36, 0x0, 0x73, 0x46, 0xf8, 0x76, 0x81, 0x37, 0xa0, 0x26, 0x72, 0xb4, 0xa7, 0x4d, 0x2c, 0x34, 0x22, 0xf3, 0xe0, 0x8c, 0x9, 0x2, 0x31, 0xf2, 0x28, 0xe4, 0xe2, 0x7f, 0xea, 0x13, 0x64, 0x47, 0x6c, 0x83, 0x36, 0x6d, 0xd2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xd, 0x10, 0x17, 0x14, 0x18, 0x1d, 0x1a, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x10, 0x13, 0x35, 0x2f, 0x38, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0x19, 0x17, 0x1b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x24, 0x1e, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xb, 0x10, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x85, 0xbb, 0x9b, 0xdf, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xff, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0xff, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0xff, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0x31, 0x64, 0xfe, 0xeb, 0x0, 0x0, 0x0, 0x8d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xc8, 0x35, 0x62, 0x3, 0x41, 0x14, 0x4, 0xd1, 0xee, 0x21, 0x33, 0x33, 0xdb, 0xf7, 0xbf, 0x93, 0x99, 0x49, 0xcc, 0xd2, 0x7c, 0x2d, 0x53, 0xa6, 0x58, 0x5b, 0x59, 0x3d, 0x2, 0xc, 0x3, 0x20, 0x61, 0x20, 0x5c, 0x9, 0xc6, 0x74, 0x11, 0x20, 0x2c, 0x86, 0xbd, 0xec, 0x63, 0xa1, 0xd9, 0xa7, 0x47, 0x9a, 0x92, 0x7f, 0xda, 0x83, 0xa1, 0xcd, 0x60, 0xb2, 0xfa, 0xc7, 0x33, 0xf6, 0xb, 0xb0, 0x2e, 0xb4, 0xce, 0x2e, 0x17, 0x20, 0x2f, 0x82, 0x5d, 0x66, 0x2f, 0xb2, 0x20, 0xd4, 0x50, 0xc3, 0x19, 0x59, 0x1, 0xe3, 0xb, 0xa0, 0xa6, 0x34, 0xe7, 0x9c, 0x65, 0xa0, 0xe5, 0x9d, 0x7b, 0x3b, 0xe4, 0x38, 0x79, 0x27, 0xd2, 0xa2, 0xbb, 0x22, 0xd9, 0x8b, 0x7e, 0x43, 0x44, 0x5e, 0x8, 0x75, 0x67, 0x66, 0x1f, 0x3b, 0x0, 0x5a, 0x67, 0x7a, 0xfa, 0xe0, 0x9, 0xb8, 0x99, 0x3a, 0x44, 0xd8, 0xaf, 0xd7, 0x63, 0x10, 0x95, 0xe6, 0x1e, 0x57, 0xc1, 0x90, 0xf7, 0xdc, 0x9d, 0x9f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_comment_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf9, 0x43, 0xbb, 0x7f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x9, 0x2, 0xe, 0x16, 0x22, 0xbe, 0xef, 0xc2, 0xe1, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x1, 0x4a, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0xd7, 0xbf, 0x4b, 0xdb, 0x41, 0x1c, 0xc6, 0xf1, 0xd7, 0x37, 0x51, 0x4, 0x3, 0xa, 0xa2, 0x20, 0xd2, 0xe2, 0xe2, 0x64, 0x41, 0xdc, 0xdc, 0xac, 0xe0, 0x54, 0xdc, 0xb2, 0xe6, 0x2f, 0x10, 0x1a, 0xf0, 0x4f, 0x11, 0x22, 0xf8, 0x17, 0x64, 0xcd, 0x26, 0x9d, 0x1c, 0x74, 0x73, 0x13, 0x21, 0x4e, 0x2e, 0xa5, 0xa5, 0x14, 0xac, 0x82, 0x82, 0x62, 0xd0, 0x7c, 0xd3, 0xa1, 0x77, 0x18, 0x35, 0xfe, 0x48, 0xa4, 0xdb, 0x3d, 0x70, 0xdc, 0xf2, 0x79, 0xde, 0x77, 0xf7, 0x39, 0x38, 0xee, 0xc9, 0xdc, 0x2b, 0x43, 0x1, 0xc5, 0x30, 0x67, 0x1e, 0xaa, 0x83, 0x1c, 0xed, 0x30, 0x77, 0x74, 0x15, 0x15, 0x30, 0x8a, 0x9, 0x4c, 0x61, 0xc, 0xc3, 0x8f, 0x0, 0xb7, 0xb8, 0xc4, 0x29, 0xce, 0x71, 0x8d, 0x3c, 0xae, 0x5a, 0xc2, 0x5c, 0xa3, 0x5a, 0x59, 0xc7, 0x17, 0x7c, 0xd0, 0x5b, 0x3f, 0xf1, 0xad, 0x5c, 0xab, 0x6f, 0xe3, 0x4, 0x57, 0x59, 0x58, 0x69, 0xb6, 0x51, 0xad, 0x6c, 0x62, 0xed, 0x77, 0xf3, 0xf0, 0x87, 0x17, 0x34, 0xfd, 0x69, 0xf1, 0x23, 0x76, 0xca, 0xb5, 0xfa, 0x6, 0xbe, 0xc7, 0x33, 0x4f, 0x61, 0xe5, 0x35, 0x33, 0x84, 0x9a, 0x95, 0xe0, 0x29, 0xc6, 0x66, 0x95, 0xc2, 0x78, 0xab, 0x62, 0x7d, 0x16, 0x1, 0x45, 0xfd, 0xab, 0x18, 0x1, 0x9d, 0x78, 0x25, 0x83, 0xa8, 0xe0, 0x9d, 0x4a, 0x80, 0x4, 0x48, 0x80, 0x7f, 0x1a, 0xea, 0xf1, 0xda, 0xc, 0xe, 0x38, 0xd8, 0xdf, 0x5b, 0x7d, 0x8b, 0x69, 0x69, 0xf9, 0xf3, 0x6e, 0xba, 0x85, 0x4, 0x48, 0x80, 0x4, 0x48, 0x80, 0x4, 0x48, 0x80, 0x4, 0xf8, 0xaf, 0x80, 0xac, 0x47, 0x46, 0xec, 0x7b, 0x7, 0xf9, 0x0, 0xde, 0x3c, 0x2, 0x72, 0xdc, 0xa0, 0xd5, 0x87, 0xb9, 0x15, 0x3c, 0x79, 0x21, 0x44, 0xd9, 0x33, 0x34, 0xbb, 0x3f, 0x4f, 0xaf, 0x7c, 0xb0, 0x9a, 0xc1, 0xd3, 0x8e, 0xc9, 0x75, 0x1c, 0xb, 0x8d, 0x6a, 0x65, 0xb, 0xf3, 0x2f, 0x34, 0x37, 0xc7, 0x71, 0xb9, 0x56, 0xff, 0x8a, 0x23, 0x5c, 0x64, 0x5d, 0x11, 0x6e, 0xc, 0x33, 0x98, 0xc4, 0xc8, 0x33, 0xe1, 0xbb, 0x85, 0x3f, 0xf8, 0x15, 0x72, 0x74, 0x3b, 0x7b, 0xd4, 0xd0, 0xa1, 0x98, 0x7, 0x9f, 0xd9, 0x41, 0x27, 0x1c, 0xf9, 0x6e, 0xc0, 0xc6, 0x3f, 0xd5, 0x5f, 0x9d, 0x54, 0x4e, 0x15, 0xfd, 0xeb, 0xb4, 0x4f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x0, 0x6f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x96, 0x42, 0x2b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x77, 0x2f, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xae, 0x6d, 0x5b, 0xae, 0x6d, 0x5b, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0x8b, 0xc7, 0x0, 0x0, 0x0, 0x25, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x6, 0x8, 0x9, 0x2, 0xc, 0x1e, 0x33, 0x41, 0x46, 0xd, 0x31, 0x9a, 0xe3, 0xff, 0x5, 0x24, 0xb4, 0xe2, 0x39, 0xf4, 0x44, 0xa, 0x47, 0x42, 0x45, 0x3d, 0xf8, 0x2a, 0xcd, 0x11, 0x3f, 0xd3, 0xfd, 0x2b, 0xb1, 0x1b, 0xa4, 0x4f, 0x0, 0x0, 0x0, 0x90, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xca, 0xb5, 0x75, 0xc4, 0x50, 0x0, 0x5, 0xd1, 0xf7, 0x49, 0xcc, 0xcc, 0xdc, 0x7f, 0x8b, 0x2b, 0xe6, 0xc8, 0xb1, 0x75, 0xc3, 0x39, 0x3, 0x80, 0x50, 0xc6, 0xc5, 0x84, 0x33, 0x4a, 0x30, 0x91, 0x64, 0x45, 0xd5, 0xf4, 0x89, 0xa6, 0x2a, 0xb2, 0x4, 0x48, 0x86, 0x69, 0xd9, 0xce, 0xc2, 0xb6, 0x4c, 0x43, 0x82, 0xeb, 0xf9, 0x4e, 0xb0, 0x71, 0x7c, 0xcf, 0x5, 0xf, 0xa3, 0xe0, 0x10, 0x85, 0x1c, 0x22, 0x76, 0xce, 0xe0, 0xc4, 0x2, 0x49, 0x7a, 0xd, 0x69, 0x2, 0xa1, 0xff, 0xb3, 0x70, 0xda, 0x42, 0x76, 0xf8, 0x73, 0xf8, 0xc2, 0x17, 0x92, 0xf4, 0x19, 0xf2, 0x6b, 0xc8, 0x13, 0xf0, 0xa2, 0x3c, 0x43, 0x59, 0x70, 0xb8, 0x55, 0x7d, 0x2c, 0x4e, 0x5d, 0xb9, 0x90, 0x9a, 0xb6, 0xeb, 0x9d, 0x45, 0xdf, 0xb5, 0x8d, 0x4, 0xd0, 0x66, 0x68, 0xf5, 0x74, 0xa2, 0xb7, 0x43, 0x43, 0x31, 0x91, 0x98, 0x48, 0x16, 0x82, 0x49, 0x78, 0x1b, 0x1, 0xf, 0xa7, 0x50, 0x68, 0x35, 0xb8, 0x84, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_default_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x39, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0xe, 0xb, 0x10, 0xe, 0xb, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x24, 0x1e, 0x27, 0x16, 0x12, 0x19, 0xff, 0xff, 0xff, 0x2b, 0x4d, 0xfd, 0x66, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x46, 0x47, 0x3f, 0x2b, 0x11, 0x3, 0xfd, 0xd3, 0xcd, 0x2a, 0x73, 0x45, 0xf8, 0x3d, 0x3f, 0x57, 0xda, 0x84, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x12, 0x7b, 0xbc, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x64, 0x2, 0x2, 0x46, 0x8, 0xc9, 0xcc, 0xc2, 0xca, 0xc6, 0xc0, 0x8f, 0x4, 0xd8, 0x39, 0x98, 0x59, 0x19, 0x50, 0x80, 0x0, 0x27, 0x17, 0xaa, 0x0, 0x83, 0x20, 0x37, 0x9a, 0x0, 0x3f, 0xd3, 0xc0, 0x8, 0xf0, 0xa0, 0x9, 0xf0, 0xf2, 0x61, 0x3a, 0x1d, 0xc3, 0x73, 0xe8, 0xde, 0x7, 0x0, 0x89, 0x4d, 0x2, 0xf2, 0x16, 0xd3, 0x74, 0x45, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0xe, 0xb, 0x10, 0xe, 0xb, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x12, 0x19, 0x0, 0x0, 0x0, 0x19, 0x15, 0x1c, 0x24, 0x1e, 0x27, 0x16, 0x12, 0x19, 0x76, 0x9, 0xd2, 0x13, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x46, 0x47, 0x3f, 0x2b, 0x11, 0x3, 0xfd, 0xd3, 0xcd, 0x2a, 0x73, 0x45, 0xf8, 0x3d, 0x3f, 0x57, 0xda, 0x84, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x64, 0x2, 0x2, 0x46, 0x8, 0xc9, 0xcc, 0xc2, 0xca, 0xc6, 0xc0, 0x8f, 0x4, 0xd8, 0x39, 0x98, 0x59, 0x19, 0x50, 0x80, 0x0, 0x27, 0x17, 0x3, 0x2a, 0x10, 0xe4, 0x46, 0x13, 0xe0, 0x67, 0x1a, 0x18, 0x1, 0x1e, 0x34, 0x1, 0x5e, 0x3e, 0xc, 0xa7, 0x63, 0x78, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x4d, 0x2, 0xf2, 0xa2, 0x23, 0x3b, 0xc4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_default_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xb7, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x9d, 0x91, 0x3d, 0x6a, 0x42, 0x61, 0x10, 0x45, 0xcf, 0x8c, 0x4f, 0x22, 0x7c, 0x58, 0x84, 0x14, 0x2e, 0xc0, 0xf5, 0x64, 0x17, 0xba, 0x3, 0x2d, 0x4, 0x41, 0x17, 0x96, 0x2a, 0x65, 0x5c, 0x43, 0x7a, 0xb, 0x53, 0xfa, 0xc3, 0x63, 0x9c, 0x6b, 0xf1, 0x4, 0xf9, 0x24, 0x31, 0x21, 0xb7, 0x1b, 0xe6, 0x14, 0x33, 0xe7, 0x1a, 0x80, 0xc, 0xc7, 0x1, 0xa7, 0x4b, 0x2, 0x49, 0x9a, 0xc0, 0x34, 0xc2, 0xbb, 0x11, 0xae, 0x58, 0x92, 0x4, 0x2d, 0x2d, 0x61, 0xb2, 0x99, 0xf8, 0x26, 0xfd, 0xaf, 0xe1, 0xdb, 0x62, 0xca, 0x89, 0x40, 0x88, 0x35, 0x13, 0xb6, 0x15, 0xb0, 0x7d, 0x99, 0x68, 0xfd, 0xae, 0xa2, 0x1e, 0x42, 0xcc, 0x9, 0x4a, 0x5, 0x94, 0x71, 0xd8, 0xfc, 0xa8, 0x67, 0xf5, 0x1d, 0x40, 0x34, 0xec, 0x2b, 0x60, 0xff, 0xd9, 0x48, 0x3, 0x1a, 0xdc, 0x79, 0x14, 0xbf, 0x3d, 0xf6, 0x4f, 0x80, 0xdf, 0x80, 0xfc, 0x2b, 0x60, 0x77, 0x16, 0x3a, 0x13, 0x76, 0x24, 0x48, 0x7, 0x28, 0x2c, 0x89, 0x6a, 0x1d, 0xc3, 0xe5, 0xae, 0x7c, 0xd0, 0x92, 0x3f, 0xa8, 0xb6, 0x53, 0xd9, 0xac, 0x5e, 0x39, 0x10, 0x8f, 0xcb, 0x4a, 0x3b, 0x5b, 0x67, 0x12, 0x80, 0xa7, 0xea, 0xbc, 0x6b, 0xdd, 0x17, 0xbe, 0x3f, 0x4f, 0x23, 0x27, 0x82, 0x63, 0x73, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x8a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x81, 0xff, 0x8c, 0xff, 0x99, 0xff, 0xb3, 0x2, 0x21, 0x3b, 0x4, 0x82, 0xd8, 0x40, 0x11, 0x46, 0x88, 0xa4, 0xf8, 0x7f, 0x49, 0x20, 0x14, 0xff, 0x2f, 0xa, 0x84, 0x8, 0xb6, 0xe0, 0x7f, 0x6e, 0xa0, 0x22, 0x90, 0x92, 0x92, 0xff, 0xd8, 0x60, 0xe5, 0x9b, 0xb6, 0x15, 0xff, 0x79, 0xc1, 0x4a, 0xb0, 0x83, 0x17, 0xc2, 0x29, 0xff, 0x1b, 0xe, 0x1, 0x4d, 0x61, 0xc6, 0xa1, 0x80, 0x5b, 0xf9, 0xf, 0x63, 0xe9, 0x77, 0xa0, 0x45, 0xac, 0xc, 0x38, 0x0, 0xd8, 0x72, 0xa0, 0x5b, 0xd8, 0xf1, 0x2b, 0x10, 0x27, 0xa4, 0x40, 0x92, 0x7c, 0x5, 0x8, 0x2b, 0xc8, 0x77, 0xe4, 0xb, 0xe1, 0xd2, 0x6f, 0x78, 0xbc, 0xf9, 0x87, 0x17, 0x18, 0x50, 0x7, 0x40, 0x1, 0x85, 0x23, 0xa8, 0x4b, 0xbf, 0x3, 0xc3, 0x91, 0x1f, 0x14, 0xd4, 0x78, 0x23, 0xb, 0x2d, 0xa0, 0xff, 0xb3, 0x23, 0x20, 0x22, 0xba, 0x1, 0x39, 0x96, 0x8a, 0xa5, 0x9b, 0x88, 0xa3, 0x56, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_position_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x6, 0xf, 0x3b, 0x3b, 0x49, 0x6e, 0xe4, 0x1e, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x90, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0xd7, 0xbd, 0x9, 0xc0, 0x20, 0x10, 0x5, 0xe0, 0x53, 0x2c, 0xdd, 0x40, 0x47, 0x70, 0x7, 0x67, 0x77, 0x7, 0x47, 0x88, 0x1b, 0xd8, 0x9b, 0xe6, 0x84, 0x53, 0x48, 0x10, 0x14, 0x52, 0xe4, 0x5d, 0x25, 0xfe, 0x7c, 0x9e, 0x58, 0x3d, 0xa2, 0xcd, 0x52, 0x7d, 0x50, 0x63, 0xb8, 0x88, 0xc8, 0x2d, 0x9e, 0x2b, 0x36, 0x65, 0x4f, 0xf2, 0x30, 0x3, 0x4b, 0x25, 0xf7, 0x1b, 0x9e, 0x73, 0x36, 0x65, 0xb5, 0xd0, 0x49, 0xb1, 0x29, 0x7b, 0x9b, 0xb2, 0xaf, 0x31, 0x34, 0x9, 0xc8, 0x67, 0x50, 0xc7, 0x9e, 0x6e, 0x96, 0xed, 0xeb, 0x69, 0x8f, 0x1b, 0xde, 0x36, 0x15, 0xaf, 0xd, 0xdd, 0xe9, 0xdd, 0x5f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x1f, 0x1, 0xe5, 0x2d, 0x3f, 0xf2, 0x5a, 0x91, 0x73, 0x66, 0x4e, 0x65, 0x1c, 0xed, 0xda, 0x52, 0x62, 0x3d, 0x91, 0x5c, 0xcf, 0x64, 0xe7, 0x4f, 0xeb, 0x6, 0x80, 0xff, 0x44, 0x93, 0xd4, 0xd9, 0xea, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x2, 0x3, 0x0, 0x0, 0x0, 0x6e, 0x13, 0x1f, 0x5, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf4, 0x3f, 0x2c, 0xf4, 0x3f, 0x2c, 0x1c, 0x3e, 0x10, 0xcd, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xe4, 0xd1, 0xf4, 0xeb, 0x59, 0x0, 0x0, 0x0, 0x30, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x0, 0x3, 0xb6, 0x55, 0xab, 0x26, 0x30, 0x88, 0x30, 0x0, 0x91, 0x42, 0xd6, 0x4a, 0xe, 0x6, 0x45, 0x7, 0x46, 0xf, 0x6, 0x25, 0x6, 0x86, 0xe, 0x20, 0x31, 0x4a, 0x80, 0x42, 0x3, 0x1c, 0x2e, 0xe0, 0x10, 0x82, 0x84, 0x15, 0x1c, 0x0, 0x0, 0x41, 0x2d, 0x2b, 0x21, 0xbb, 0xb7, 0x1a, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_node_selected_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x6, 0x0, 0x0, 0x0, 0x13, 0x7d, 0xf7, 0x96, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x17, 0xd, 0x4, 0x3b, 0xfa, 0x91, 0x2a, 0xb6, 0x0, 0x0, 0x3, 0x44, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x97, 0x3f, 0x68, 0xdc, 0x76, 0x14, 0xc7, 0xbf, 0xef, 0xa7, 0x9f, 0x7e, 0x3a, 0xdd, 0x39, 0xf6, 0xdd, 0x95, 0xb3, 0x3, 0xc1, 0x75, 0x1b, 0x70, 0xa, 0xe9, 0x98, 0x25, 0x93, 0x87, 0x52, 0x3a, 0x4, 0x2, 0xce, 0xe0, 0xd2, 0x34, 0x35, 0x94, 0x8e, 0x1d, 0xbc, 0xd5, 0x4b, 0xc7, 0x2e, 0x25, 0x63, 0x2, 0x5d, 0xa, 0xa5, 0xb1, 0x1b, 0x2, 0xf5, 0xd0, 0x40, 0xa0, 0x90, 0x10, 0x3a, 0x64, 0xca, 0x92, 0x31, 0x81, 0x24, 0xd0, 0xd2, 0x9a, 0x40, 0x2f, 0x17, 0xdf, 0x1f, 0xfb, 0x74, 0xf2, 0xe9, 0x27, 0xfd, 0x5e, 0x7, 0x49, 0xb6, 0x4e, 0xe7, 0xea, 0x86, 0x6e, 0xe5, 0x1e, 0xdc, 0x49, 0x42, 0xef, 0x7d, 0xde, 0xf7, 0x7d, 0xf5, 0x13, 0xe8, 0x47, 0xcc, 0x4c, 0x0, 0xc4, 0xbd, 0x7b, 0x4f, 0x9c, 0xb0, 0xdd, 0x71, 0xdb, 0xfb, 0x2d, 0xc7, 0xb0, 0x11, 0x5a, 0x7, 0x84, 0x4c, 0xd8, 0xb6, 0x62, 0x41, 0xc2, 0xd4, 0x67, 0x1b, 0x43, 0x59, 0xaf, 0xf9, 0x97, 0x2f, 0x5f, 0x18, 0x2, 0x30, 0xc4, 0xcc, 0xd6, 0xdd, 0x1f, 0xef, 0x9e, 0xea, 0x79, 0x83, 0x33, 0xed, 0x6e, 0xf7, 0x6c, 0xaf, 0xd7, 0x59, 0xe8, 0x7b, 0xfd, 0x72, 0x10, 0x4, 0x32, 0xb, 0x50, 0x4a, 0x85, 0x33, 0x95, 0x99, 0xc1, 0xdc, 0x5c, 0xad, 0x59, 0xaf, 0x56, 0x7f, 0x9f, 0xab, 0x94, 0x5f, 0xad, 0x7e, 0xbe, 0x7a, 0x40, 0x5b, 0x5b, 0xf7, 0x2b, 0x41, 0xbb, 0xf9, 0x6e, 0xa7, 0xd7, 0x5e, 0xb9, 0x72, 0x75, 0xe5, 0x86, 0x72, 0xa4, 0x85, 0x82, 0x8, 0x86, 0x61, 0xf4, 0xcb, 0x9d, 0x47, 0x1b, 0xb5, 0xb9, 0xfa, 0x23, 0x55, 0x5f, 0xf8, 0x43, 0xea, 0x4e, 0xb7, 0xdc, 0x7c, 0xf3, 0xfa, 0xdc, 0xc7, 0xeb, 0x1f, 0xdc, 0x64, 0x66, 0x71, 0xb8, 0x37, 0xbb, 0x2b, 0x1d, 0xb, 0x44, 0x23, 0x13, 0x80, 0x99, 0x11, 0xe, 0x23, 0x50, 0x65, 0x6f, 0x71, 0xf5, 0x93, 0x95, 0x9b, 0x3f, 0x6f, 0xff, 0xb6, 0x76, 0x9a, 0x9c, 0xa6, 0x8, 0x42, 0x5f, 0x79, 0xfd, 0x83, 0x79, 0xa7, 0x24, 0x5, 0xfc, 0xfa, 0x2e, 0x9, 0x42, 0xa8, 0xd, 0xc2, 0x20, 0x1a, 0xfd, 0x69, 0x3, 0x12, 0x4, 0xf8, 0xf5, 0x5d, 0xa7, 0x24, 0x85, 0xd7, 0x3f, 0x98, 0xf, 0x42, 0x5f, 0x9, 0xc3, 0x46, 0xc, 0xfc, 0x81, 0xb, 0x0, 0x91, 0x66, 0x30, 0x1b, 0x10, 0x0, 0x98, 0x6c, 0x7b, 0x80, 0xc0, 0x60, 0x66, 0x44, 0x21, 0x3, 0x0, 0x6, 0xfe, 0xc0, 0x35, 0x6c, 0x84, 0xd0, 0x5a, 0x53, 0x84, 0x88, 0x0, 0x80, 0x13, 0xd9, 0xcc, 0xc, 0x88, 0xb8, 0x10, 0x0, 0x40, 0x0, 0x1b, 0x0, 0xcc, 0x0, 0xc7, 0x39, 0x11, 0x22, 0xd2, 0x5a, 0x93, 0xb4, 0x6d, 0x9b, 0x11, 0x25, 0x89, 0x86, 0xc1, 0x20, 0x80, 0x18, 0xa0, 0xe3, 0xee, 0x47, 0xd0, 0xcc, 0x31, 0xad, 0x91, 0x79, 0xa3, 0x40, 0x4, 0x80, 0x91, 0xe6, 0xe5, 0x83, 0x73, 0x37, 0x64, 0xee, 0x2e, 0xd8, 0x24, 0x9, 0xc4, 0xc7, 0xa, 0xb2, 0x6a, 0x8a, 0x0, 0x1c, 0xf7, 0xce, 0x5e, 0xc4, 0xc5, 0xe6, 0x18, 0x92, 0x57, 0x36, 0x2, 0x30, 0x26, 0x33, 0x7b, 0xe2, 0x1e, 0x71, 0xe2, 0xb, 0x33, 0x68, 0xf2, 0x8, 0xd9, 0x16, 0x94, 0xd1, 0x13, 0xff, 0x9b, 0x49, 0xa, 0xc0, 0x6, 0x6c, 0x72, 0xf6, 0x8f, 0xbb, 0x58, 0xe0, 0x41, 0x32, 0x77, 0x22, 0x1a, 0xc9, 0xf2, 0x49, 0x87, 0x89, 0xcf, 0x8a, 0x14, 0xc4, 0x0, 0x93, 0x14, 0x66, 0xba, 0xd1, 0xf1, 0xec, 0xa6, 0x18, 0x60, 0xe2, 0xfa, 0x91, 0xde, 0xd9, 0xae, 0x34, 0xf9, 0x31, 0x52, 0xba, 0x1e, 0x28, 0x5d, 0x4c, 0x94, 0xfa, 0x99, 0xbc, 0xf, 0x45, 0xa, 0x4c, 0x5c, 0x38, 0xd6, 0xc9, 0xe0, 0xe8, 0x89, 0xe4, 0xad, 0x1d, 0x5f, 0xca, 0x9c, 0x7f, 0x15, 0x8b, 0x63, 0xdc, 0xc4, 0x7c, 0x8f, 0x8c, 0x91, 0xb9, 0x93, 0x71, 0xc0, 0xd7, 0xdf, 0x7c, 0xf5, 0xea, 0xd9, 0x8b, 0xa7, 0x85, 0x1d, 0xcf, 0x9f, 0x7b, 0x1f, 0xdf, 0xdf, 0xfa, 0x76, 0xf1, 0x44, 0xc0, 0xd9, 0xa5, 0x65, 0x58, 0xc2, 0x2e, 0x4, 0x2c, 0x2d, 0xbe, 0xf3, 0xef, 0xa, 0x3c, 0xaf, 0x8f, 0xde, 0x7e, 0xb7, 0x10, 0xe0, 0x79, 0xfd, 0x91, 0x6b, 0x81, 0xff, 0x18, 0x53, 0xc0, 0x14, 0x30, 0x5, 0x4c, 0x1, 0x53, 0xc0, 0x14, 0x30, 0x5, 0xfc, 0xff, 0x0, 0x5a, 0x6b, 0x82, 0x75, 0xe2, 0xe7, 0xcf, 0xc9, 0x91, 0xe6, 0x58, 0x19, 0x5, 0x16, 0x2c, 0x6, 0x0, 0x5b, 0xca, 0x89, 0xf5, 0x69, 0x4e, 0x5a, 0x23, 0x1c, 0xe5, 0x44, 0x8e, 0x52, 0x1, 0x0, 0x94, 0xdd, 0x99, 0x89, 0x80, 0x34, 0xc7, 0x51, 0x2a, 0x70, 0x94, 0x13, 0x49, 0x25, 0xdd, 0xa0, 0xe4, 0x96, 0x5b, 0xc3, 0x43, 0xcd, 0x1b, 0x9b, 0x9f, 0x5e, 0xbc, 0x71, 0x9d, 0x1e, 0xf, 0xfc, 0x3, 0xe8, 0x30, 0x1c, 0xd9, 0x74, 0xd9, 0x52, 0xa2, 0xec, 0x9e, 0xc2, 0xc6, 0xe6, 0xd5, 0x8b, 0xc3, 0x43, 0xcd, 0x25, 0xb7, 0xdc, 0x52, 0xd2, 0xd, 0x68, 0x6b, 0xeb, 0x7e, 0x65, 0xef, 0xaf, 0x97, 0xcb, 0xdd, 0xfd, 0xce, 0x87, 0x9f, 0x7d, 0x71, 0xe9, 0xba, 0xe3, 0xda, 0x85, 0x4e, 0xc, 0x7d, 0xcd, 0x3f, 0xfd, 0xf0, 0xeb, 0x66, 0x75, 0xb6, 0xf6, 0xf0, 0xad, 0xb7, 0x97, 0x5f, 0x12, 0x33, 0x5b, 0xb7, 0xbf, 0xbb, 0x3d, 0xdb, 0xea, 0x74, 0x97, 0x3a, 0xdd, 0xee, 0x7b, 0x3, 0xdf, 0x9b, 0xd7, 0x5a, 0xdb, 0xc6, 0x8c, 0x7e, 0xad, 0xa, 0x21, 0x60, 0xdb, 0xb6, 0x2e, 0xbb, 0x95, 0xd7, 0xb5, 0x6a, 0xf5, 0x79, 0xa3, 0x56, 0xfd, 0xf3, 0xda, 0x97, 0xd7, 0xf6, 0x29, 0xdd, 0xbd, 0x6f, 0x6f, 0x3f, 0x28, 0xf5, 0xff, 0x6e, 0x56, 0xb4, 0x19, 0x2a, 0x66, 0x73, 0xe2, 0xe3, 0x25, 0x12, 0xc6, 0x16, 0x4e, 0x30, 0x73, 0x7a, 0xc1, 0x5b, 0x5f, 0xff, 0xe8, 0x10, 0xc0, 0xd1, 0xf6, 0x4, 0xcc, 0x4c, 0x3b, 0x3b, 0x3b, 0xa2, 0xd1, 0x68, 0x14, 0x8e, 0xd0, 0x6a, 0xb5, 0x78, 0x6d, 0x6d, 0xcd, 0x10, 0xc5, 0xdb, 0xba, 0x7f, 0x0, 0xb2, 0x1f, 0xaf, 0x82, 0x62, 0x7a, 0x69, 0xbb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x24, 0xa3, 0x7, 0xa4, 0x0, 0x0, 0x1, 0x5f, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0xb2, 0xcc, 0xae, 0xa0, 0xbb, 0x9c, 0x92, 0xa6, 0x9a, 0x91, 0xa4, 0x95, 0x8c, 0x9e, 0xaf, 0x9f, 0xaf, 0xa0, 0x94, 0xa5, 0x8e, 0x84, 0x95, 0x7f, 0x75, 0x84, 0x73, 0x6a, 0x78, 0x6d, 0x64, 0x72, 0xa2, 0xa2, 0xb9, 0x9c, 0x90, 0xa2, 0x8b, 0x81, 0x90, 0xdc, 0xda, 0xce, 0xe2, 0xe1, 0xd2, 0x9d, 0x91, 0xa9, 0x85, 0x7c, 0x8c, 0xdb, 0xd9, 0xce, 0xdb, 0xd9, 0xcd, 0xda, 0xce, 0xe0, 0xde, 0xd5, 0xe3, 0xdf, 0xd6, 0xe4, 0x97, 0x8d, 0xa0, 0x7a, 0x70, 0x7f, 0xdb, 0xd0, 0xdf, 0xdb, 0xd0, 0xe1, 0xda, 0xd0, 0xe1, 0x70, 0x67, 0x75, 0xd8, 0xcb, 0xde, 0xda, 0xcf, 0xdf, 0xdb, 0xce, 0xe1, 0xdb, 0xcf, 0xe1, 0xdb, 0xd0, 0xe0, 0xda, 0xcf, 0xe0, 0xd8, 0xcc, 0xde, 0x90, 0x87, 0x99, 0x6d, 0x67, 0x72, 0xd7, 0xcc, 0xdf, 0xda, 0xce, 0xdf, 0xd8, 0xcb, 0xdf, 0xd7, 0xca, 0xde, 0xd9, 0xcc, 0xdf, 0xd9, 0xcd, 0xdf, 0xd6, 0xc9, 0xdd, 0xd9, 0xcd, 0xde, 0xd6, 0xc8, 0xdc, 0xd5, 0xc8, 0xdc, 0xd7, 0xcb, 0xdd, 0xd7, 0xca, 0xdd, 0xd5, 0xc7, 0xdc, 0xd3, 0xc6, 0xdb, 0xd5, 0xc9, 0xdc, 0xd5, 0xc9, 0xdd, 0xd6, 0xc9, 0xdc, 0xd4, 0xc6, 0xdb, 0xd3, 0xc5, 0xdb, 0xd5, 0xc8, 0xdb, 0xd4, 0xc8, 0xdc, 0xd3, 0xc4, 0xd9, 0xd4, 0xc6, 0xda, 0xd2, 0xc3, 0xd9, 0xd3, 0xc5, 0xda, 0xd2, 0xc5, 0xd9, 0xd3, 0xc5, 0xd9, 0xd2, 0xc5, 0xda, 0xd1, 0xc2, 0xd9, 0xd2, 0xc4, 0xd8, 0xd2, 0xc4, 0xd9, 0xd0, 0xc2, 0xd9, 0xd0, 0xc1, 0xd7, 0xd0, 0xc2, 0xd7, 0xd0, 0xc2, 0xd8, 0xd1, 0xc2, 0xd7, 0xcf, 0xc1, 0xd7, 0xd0, 0xc2, 0xd6, 0xcf, 0xc1, 0xd6, 0xcf, 0xc2, 0xd7, 0xcf, 0xc0, 0xd7, 0xce, 0xbf, 0xd6, 0xce, 0xc0, 0xd5, 0xce, 0xc0, 0xd6, 0xce, 0xbf, 0xd5, 0xcd, 0xbf, 0xd5, 0xcd, 0xbe, 0xd5, 0xcd, 0xbe, 0xd4, 0xcc, 0xbd, 0xd5, 0xcc, 0xbd, 0xd4, 0xcc, 0xbc, 0xd4, 0x47, 0x40, 0x4a, 0x1d, 0x1a, 0x1f, 0x69, 0x5f, 0x6f, 0x4a, 0x42, 0x4f, 0x5e, 0x54, 0x63, 0x3b, 0x34, 0x3f, 0x5e, 0x55, 0x63, 0x63, 0x59, 0x67, 0x77, 0x6d, 0x7b, 0x6d, 0x62, 0x73, 0x7f, 0x76, 0x85, 0xdb, 0xd9, 0xcd, 0xdb, 0xd8, 0xcd, 0x6d, 0x62, 0x74, 0x8f, 0x84, 0x94, 0x7f, 0x76, 0x83, 0xdb, 0xd8, 0xcd, 0xa4, 0x95, 0xa4, 0x7e, 0x74, 0x84, 0x74, 0x6b, 0x79, 0x6f, 0x66, 0x74, 0x96, 0x8a, 0xa2, 0x91, 0x88, 0x9b, 0x0, 0x0, 0x0, 0xaa, 0xaa, 0xaa, 0xbf, 0xbf, 0xbf, 0xf0, 0xc9, 0xec, 0x71, 0x0, 0x0, 0x0, 0x75, 0x74, 0x52, 0x4e, 0x53, 0x1, 0x3, 0xa, 0x13, 0x1a, 0x1c, 0x1d, 0x10, 0x2b, 0x4d, 0x64, 0x6e, 0x72, 0xb, 0x2c, 0x6a, 0xfc, 0xff, 0x15, 0x52, 0xfd, 0xff, 0xe2, 0xe2, 0xe2, 0x1b, 0x68, 0xe2, 0xe2, 0xe2, 0x71, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0x1e, 0x72, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0x6b, 0xc7, 0x56, 0xfe, 0xff, 0xc7, 0x30, 0x74, 0xfe, 0x11, 0x57, 0x6d, 0x72, 0x16, 0x1c, 0x0, 0x3, 0x4, 0x35, 0xf5, 0x4, 0x26, 0x0, 0x0, 0x1, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0xd0, 0x35, 0x82, 0x1c, 0x31, 0x14, 0x84, 0xe1, 0xbf, 0xa4, 0xd7, 0x32, 0x33, 0xb3, 0xf, 0x63, 0x8e, 0xcd, 0x10, 0xcd, 0x99, 0x1c, 0x3a, 0x75, 0xe8, 0xcc, 0xbe, 0x8e, 0x99, 0x79, 0xb9, 0x49, 0x6f, 0x49, 0xd3, 0xcb, 0x7b, 0x82, 0xa9, 0xac, 0x3e, 0xb1, 0xc, 0xad, 0x4, 0xf0, 0x95, 0x98, 0xd2, 0x6, 0x68, 0xec, 0x60, 0xa9, 0xac, 0x52, 0xb2, 0x6a, 0xf3, 0x92, 0x3d, 0x9a, 0xdf, 0x2f, 0x39, 0x20, 0xf7, 0xb9, 0x7d, 0x6e, 0xbf, 0xcf, 0x62, 0xea, 0xaa, 0x96, 0x24, 0x69, 0xce, 0xbe, 0xd9, 0x55, 0x4d, 0xef, 0x5b, 0xd8, 0xbb, 0x90, 0x9a, 0xa6, 0x39, 0x21, 0xf6, 0x5d, 0xb5, 0x77, 0xa9, 0x8a, 0x7, 0x35, 0xd5, 0xc0, 0x19, 0x32, 0x9f, 0xcf, 0x1b, 0x10, 0x3d, 0x9c, 0x1, 0x20, 0x2, 0x6, 0x84, 0xbf, 0x24, 0x9a, 0x44, 0x73, 0xac, 0x80, 0x4e, 0x80, 0x5c, 0x4e, 0x28, 0x60, 0x2, 0xc9, 0x3d, 0xa8, 0x40, 0x25, 0xe6, 0x0, 0xe, 0xe4, 0x2, 0x51, 0x1c, 0x2, 0x40, 0x5, 0x82, 0xa4, 0x2c, 0x3c, 0x76, 0xc3, 0x12, 0x51, 0xfb, 0x9e, 0xba, 0xdb, 0x3b, 0x6c, 0x2a, 0x45, 0xd8, 0xaf, 0x3c, 0x86, 0x3e, 0x57, 0x31, 0xc7, 0x7e, 0x98, 0x51, 0x25, 0x39, 0xca, 0x15, 0x1a, 0x3, 0x2, 0x8f, 0xc0, 0x70, 0xa, 0x20, 0x1c, 0x2f, 0x10, 0x87, 0x2f, 0x1c, 0x66, 0xec, 0xe, 0x1b, 0x33, 0x81, 0xd, 0x99, 0xc0, 0x55, 0xfd, 0x8, 0x5e, 0x4a, 0xe0, 0x82, 0x1b, 0xf3, 0x68, 0xc, 0x62, 0x7e, 0xaf, 0x4d, 0xdf, 0x79, 0x15, 0xf2, 0x30, 0xe3, 0xfa, 0x1b, 0xab, 0xc3, 0x3d, 0x59, 0xe3, 0x99, 0xa0, 0xe4, 0xf7, 0xbc, 0xb6, 0xd6, 0xa4, 0x97, 0x65, 0xc6, 0x63, 0xf7, 0xd6, 0x66, 0x62, 0x1f, 0x9f, 0xe9, 0x5, 0x30, 0x72, 0xef, 0xe2, 0x8c, 0xf9, 0xf3, 0x83, 0x23, 0xbd, 0x3e, 0xf, 0x2c, 0x6e, 0xbb, 0xf7, 0xff, 0x5, 0x5f, 0xfe, 0xb3, 0x14, 0x31, 0xf4, 0x31, 0x33, 0xfa, 0x41, 0xd3, 0xe9, 0xe7, 0x22, 0x6, 0x0, 0xb4, 0xb3, 0x74, 0xef, 0x4f, 0xde, 0x59, 0x95, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char graph_port_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x6, 0x0, 0x0, 0x0, 0x8d, 0x32, 0xcf, 0xbd, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xc6, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x6d, 0x90, 0x3d, 0x4e, 0xc4, 0x30, 0x14, 0x84, 0xbf, 0xb1, 0xa8, 0x92, 0x48, 0x39, 0x46, 0xe2, 0x9a, 0x9f, 0x12, 0xc1, 0xbd, 0x16, 0x25, 0x4b, 0x41, 0xb, 0x67, 0xa1, 0xa7, 0xa2, 0x63, 0xa1, 0x76, 0x94, 0x5b, 0x58, 0x72, 0x52, 0x3e, 0xd3, 0xc4, 0x11, 0xda, 0xe5, 0xab, 0x46, 0xa3, 0xd1, 0x68, 0xde, 0x13, 0x1b, 0x21, 0x84, 0x7, 0x49, 0x3, 0x70, 0xbb, 0x59, 0x5f, 0xc0, 0x5b, 0xdf, 0xf7, 0xef, 0x0, 0x2, 0x98, 0xa6, 0xe9, 0x0, 0x1c, 0x1, 0xc7, 0x19, 0x39, 0xe7, 0x67, 0xef, 0xfd, 0xa0, 0xad, 0xe9, 0x3, 0x70, 0x6d, 0xdb, 0x52, 0xd7, 0x35, 0x0, 0xcb, 0xb2, 0x10, 0x63, 0x4, 0x30, 0xe7, 0xdc, 0xe3, 0x95, 0xa4, 0xb1, 0x84, 0x9a, 0xa6, 0xd9, 0x9b, 0x8a, 0x8e, 0x31, 0x3a, 0x33, 0x1b, 0x1d, 0x70, 0xd, 0xec, 0x4d, 0x7f, 0xa9, 0xaa, 0xaa, 0xc8, 0x1b, 0x7, 0xe4, 0x8b, 0xc4, 0x25, 0xd9, 0x1, 0x3f, 0x65, 0xd3, 0x39, 0xeb, 0xba, 0x16, 0xf9, 0xed, 0x80, 0xd7, 0x6d, 0xb, 0x29, 0x25, 0xcc, 0xc, 0x33, 0x23, 0xa5, 0xb4, 0x1f, 0x23, 0x69, 0x14, 0x40, 0x8, 0xe1, 0x28, 0xe9, 0xf0, 0xcf, 0x7b, 0x2c, 0xe7, 0xfc, 0xe4, 0xbd, 0x7f, 0x51, 0x71, 0xe6, 0x79, 0xbe, 0x37, 0xb3, 0x1, 0xb8, 0xdb, 0x76, 0x9f, 0x24, 0x8d, 0x5d, 0xd7, 0x7d, 0x2, 0xfc, 0x2, 0xfb, 0x83, 0x50, 0x87, 0x89, 0x31, 0xee, 0x78, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x72, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xce, 0x41, 0xa, 0x83, 0x30, 0x10, 0x85, 0xe1, 0xb9, 0x98, 0xe2, 0x9, 0x3c, 0x50, 0xe9, 0xc6, 0x83, 0x45, 0x7b, 0x8b, 0xfc, 0x7a, 0x1, 0xd7, 0x49, 0xba, 0xd, 0xd3, 0x79, 0xd0, 0x16, 0x41, 0xfe, 0x6c, 0xe6, 0x83, 0xc0, 0x33, 0x8f, 0xf2, 0xc4, 0xc6, 0x3b, 0x5a, 0x99, 0x75, 0xc7, 0xe3, 0x49, 0xc7, 0x7f, 0xe5, 0x25, 0x30, 0x4f, 0xa2, 0xd3, 0x6b, 0x74, 0x8a, 0xfb, 0x31, 0x1a, 0x2f, 0x51, 0xfb, 0x26, 0x66, 0x35, 0x1a, 0x5e, 0xff, 0x58, 0x84, 0xd5, 0xa8, 0x37, 0x2c, 0xc6, 0x76, 0xfb, 0x9e, 0x8c, 0x19, 0x17, 0x97, 0x48, 0x44, 0xdf, 0x7, 0xad, 0x5c, 0x2e, 0x93, 0x7a, 0x7e, 0x68, 0x67, 0x74, 0x8c, 0x24, 0x1a, 0x95, 0xb4, 0xf, 0xba, 0x3f, 0x56, 0x94, 0xa6, 0x72, 0xc9, 0xf9, 0xda, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hseparator_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xff, 0xff, 0xff, 0x64, 0x6c, 0x1, 0xd2, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x3, 0x11, 0xc, 0x4c, 0xf2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x10, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x64, 0x60, 0x60, 0xc2, 0x40, 0x8c, 0xc, 0x0, 0x0, 0xc7, 0x0, 0xf, 0xf5, 0x92, 0x2f, 0xa7, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xff, 0xff, 0xff, 0x64, 0x6c, 0x1, 0xd2, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0x10, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x64, 0x60, 0x60, 0xc2, 0x40, 0x8c, 0xc, 0x0, 0x0, 0xc7, 0x0, 0xf, 0xf5, 0x92, 0x2f, 0xa7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hslider_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x4e, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xff, 0xff, 0xff, 0x5f, 0xd6, 0x94, 0x4d, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x68, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x1a, 0x96, 0x95, 0x6b, 0xe2, 0xd5, 0x49, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x19, 0xec, 0x6e, 0xb5, 0x88, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x85, 0xce, 0xdd, 0xe, 0x80, 0x20, 0x8, 0x5, 0x60, 0x54, 0xb4, 0x22, 0xb5, 0x34, 0x7f, 0x7a, 0xff, 0x27, 0x8d, 0xad, 0xd6, 0x6a, 0x5c, 0xf8, 0xdd, 0xc1, 0x6, 0xe7, 0x0, 0x8c, 0xa9, 0x1f, 0x9e, 0xb5, 0x41, 0xeb, 0x26, 0xe6, 0x2c, 0x1a, 0xad, 0x40, 0xcf, 0xb, 0xad, 0xf9, 0x60, 0x79, 0xa5, 0x65, 0xd6, 0x60, 0x7c, 0x28, 0xb5, 0x75, 0xd6, 0x6a, 0x9, 0xde, 0x0, 0x52, 0xe9, 0xe7, 0xa3, 0x17, 0x42, 0xb0, 0xb1, 0x9e, 0xaf, 0x1a, 0xad, 0x5c, 0x88, 0x93, 0x6d, 0xff, 0x3e, 0xdd, 0x37, 0x8e, 0x4d, 0x14, 0xef, 0xd8, 0x48, 0x89, 0x63, 0x45, 0x31, 0x51, 0x7d, 0xe8, 0x2, 0xf5, 0xf, 0x9, 0x67, 0xd9, 0x88, 0x8f, 0x5b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x57, 0x2e, 0xcb, 0x70, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x68, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x1a, 0x96, 0x95, 0x6b, 0xe2, 0xd5, 0x49, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x4f, 0x83, 0x11, 0x3, 0x1, 0x10, 0xcc, 0x39, 0xef, 0xfe, 0xb, 0x7d, 0xdb, 0xc6, 0x8e, 0x8e, 0xb, 0xf8, 0x9d, 0x70, 0x1d, 0x9c, 0x46, 0x2d, 0xcc, 0x18, 0xea, 0x11, 0x80, 0xda, 0xb7, 0x36, 0xe, 0xf2, 0xbe, 0x2f, 0x80, 0x1c, 0xb0, 0xe5, 0xa2, 0x1f, 0xa5, 0xc0, 0xea, 0x12, 0x2c, 0x4, 0x75, 0x52, 0x80, 0x38, 0x46, 0x2b, 0x65, 0x9d, 0xa7, 0x97, 0xc1, 0xf5, 0x25, 0x82, 0x7a, 0x47, 0x4a, 0x83, 0xac, 0x93, 0x33, 0x8f, 0x83, 0xaa, 0xb2, 0xb4, 0xb8, 0x18, 0xbb, 0x58, 0xff, 0x4e, 0xdb, 0x1, 0xc3, 0xf1, 0x34, 0x3b, 0x7e, 0xbc, 0xb3, 0xc3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hslider_grabber_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x1, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x85, 0xd1, 0x3f, 0x4b, 0xc3, 0x40, 0x18, 0xc7, 0xf1, 0xef, 0x25, 0xad, 0x89, 0xa9, 0x70, 0x83, 0x43, 0x5d, 0xc4, 0x21, 0x6e, 0x4a, 0x7, 0x47, 0xdf, 0x83, 0x53, 0x16, 0x17, 0xd7, 0x4e, 0xbe, 0x2, 0x5f, 0x85, 0x83, 0xa0, 0xb8, 0x38, 0xb8, 0x88, 0xd0, 0x51, 0x5d, 0x1c, 0x1c, 0x1c, 0x1c, 0x2a, 0xa2, 0xe8, 0x22, 0x2d, 0x82, 0x2d, 0x88, 0x54, 0xed, 0x3f, 0x1b, 0x9a, 0x78, 0x49, 0x1c, 0x9a, 0x54, 0x4f, 0x5, 0x6f, 0x3a, 0xee, 0xf9, 0xf0, 0x3c, 0x3f, 0x9e, 0x83, 0x7f, 0x8e, 0x18, 0xdf, 0x4c, 0x1c, 0x24, 0x5, 0x60, 0x40, 0x17, 0x9f, 0x48, 0x7, 0x26, 0xd3, 0xe5, 0x55, 0xd7, 0x93, 0x25, 0xe8, 0xde, 0xd4, 0x2b, 0xbb, 0x7, 0xbc, 0x8e, 0x88, 0x99, 0x82, 0xa9, 0xf2, 0xda, 0xe2, 0x86, 0x58, 0x78, 0xb7, 0x87, 0xf6, 0xc4, 0xdc, 0xcc, 0xd2, 0x6c, 0xfb, 0xf2, 0x8e, 0x10, 0xc0, 0x48, 0x81, 0x74, 0x3d, 0x55, 0xf4, 0x51, 0x28, 0x7c, 0x54, 0xd1, 0xf5, 0x90, 0xa3, 0x42, 0x6, 0xa, 0xb2, 0x14, 0x90, 0x0, 0x90, 0x10, 0x20, 0x4b, 0x14, 0x74, 0x20, 0x62, 0xf1, 0x3d, 0x7b, 0x24, 0xb2, 0x74, 0x19, 0x8, 0x83, 0x96, 0x39, 0x2e, 0xb, 0x82, 0x37, 0x94, 0xe, 0x6, 0xbd, 0xdb, 0xfc, 0x18, 0xe4, 0x49, 0x9e, 0xf0, 0x75, 0xd0, 0xbf, 0x3e, 0xb6, 0x22, 0x23, 0x7d, 0x9a, 0x4c, 0xce, 0xf6, 0xe8, 0xe9, 0x20, 0xb8, 0xaa, 0x6, 0xcd, 0x1c, 0x0, 0x39, 0x3e, 0x1e, 0x4f, 0xce, 0x7f, 0x76, 0x88, 0x1f, 0x1a, 0xcf, 0xa7, 0xe, 0x6, 0x6, 0xe, 0x8d, 0x23, 0xd5, 0x22, 0xd6, 0x41, 0x42, 0x77, 0x6b, 0x33, 0xaa, 0x59, 0x58, 0xc4, 0xf5, 0x9d, 0xed, 0x6c, 0xc0, 0xd7, 0x26, 0x21, 0xe, 0x7, 0x9d, 0xda, 0xf2, 0x8a, 0x1d, 0x1f, 0xae, 0xdf, 0x57, 0x19, 0xfe, 0x6, 0xa0, 0x9a, 0x2f, 0xf3, 0xed, 0xfe, 0xc5, 0x7e, 0x85, 0xce, 0x5f, 0xbf, 0x39, 0xca, 0x67, 0x21, 0x18, 0x66, 0x3b, 0x0, 0xf8, 0x4, 0x7e, 0x5c, 0x62, 0x33, 0x51, 0xf0, 0xbb, 0xff, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xf3, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x96, 0x96, 0xd7, 0x79, 0x70, 0xc6, 0xfb, 0x19, 0xef, 0x3b, 0xf, 0xa6, 0xe5, 0x31, 0x88, 0xa1, 0x2b, 0xe1, 0x4d, 0xcb, 0x9b, 0xf4, 0xa2, 0xef, 0x7f, 0x3b, 0x10, 0xf6, 0xfd, 0x9f, 0xf4, 0x2, 0xa8, 0x84, 0x17, 0x55, 0x81, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0x81, 0xb0, 0x5, 0xa8, 0xa4, 0xf3, 0x20, 0xd0, 0x22, 0x14, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x50, 0x12, 0xc, 0x81, 0xa6, 0xcc, 0x78, 0xf, 0x74, 0xb, 0xa, 0xd0, 0x98, 0xf6, 0x1, 0x59, 0xc1, 0xd4, 0xf, 0xc, 0x1a, 0xa8, 0xa, 0x94, 0xfa, 0x6f, 0x77, 0xc1, 0x15, 0x74, 0xfc, 0xef, 0xbb, 0xc7, 0xa0, 0x82, 0xaa, 0x40, 0xbc, 0x71, 0x7d, 0x3f, 0x5c, 0x41, 0xef, 0xff, 0xde, 0xa3, 0xc, 0x52, 0xa8, 0xa, 0xb8, 0x82, 0x52, 0xa7, 0xfd, 0x69, 0x5, 0x4b, 0xb7, 0xfe, 0x9f, 0xf6, 0xcf, 0x37, 0x85, 0x81, 0x7, 0x2d, 0x1c, 0x14, 0xd, 0xfb, 0x1f, 0x74, 0x82, 0x15, 0x74, 0xfe, 0x9f, 0xf8, 0x80, 0x45, 0x83, 0x81, 0x9, 0x55, 0x1, 0x23, 0x83, 0x48, 0xc5, 0x9c, 0xc9, 0xff, 0x5b, 0x81, 0x70, 0xf2, 0xff, 0x92, 0xa9, 0xc, 0xc2, 0x98, 0x41, 0xcd, 0xca, 0xa3, 0x33, 0xe1, 0x76, 0xcf, 0xff, 0x9e, 0xff, 0x13, 0xef, 0xf0, 0xe8, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0xb, 0xff, 0x39, 0x5f, 0xe6, 0x7c, 0x77, 0x8, 0x45, 0xd8, 0x8f, 0x61, 0x4d, 0x51, 0x71, 0x55, 0x2d, 0x83, 0x18, 0x90, 0x85, 0x3, 0xb0, 0x30, 0x70, 0x3, 0x75, 0xb3, 0x20, 0xb, 0x1, 0x0, 0x86, 0xe, 0x79, 0x54, 0x16, 0xbe, 0x69, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hslider_grabber_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe1, 0x7, 0xa, 0x13, 0x2f, 0x7, 0x5e, 0x49, 0xee, 0x14, 0x0, 0x0, 0x0, 0x1d, 0x69, 0x54, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x64, 0x2e, 0x65, 0x7, 0x0, 0x0, 0x0, 0xea, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x20, 0x1a, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x66, 0x9c, 0xe7, 0x7a, 0xd0, 0xe7, 0xbd, 0xcf, 0x7b, 0xd7, 0x83, 0xc6, 0x79, 0xc, 0x62, 0xe8, 0x4a, 0x78, 0x8d, 0xf3, 0x3c, 0x5f, 0xb8, 0xff, 0x77, 0x1, 0x42, 0xf7, 0xff, 0x9e, 0x2f, 0x80, 0x4a, 0x78, 0x51, 0x15, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x85, 0xee, 0xff, 0x5d, 0xf, 0x2, 0x2d, 0x42, 0x1, 0xea, 0x3e, 0xef, 0x5d, 0xe0, 0xa, 0x5c, 0xfe, 0xfb, 0xbc, 0x7, 0xba, 0x5, 0x5, 0x68, 0x78, 0x7f, 0x40, 0x56, 0xe0, 0xfd, 0x81, 0x41, 0x3, 0x55, 0x81, 0x92, 0xc7, 0x6d, 0x57, 0x24, 0x5, 0xee, 0xf7, 0x18, 0x54, 0x50, 0x15, 0x88, 0x3b, 0xae, 0xf7, 0x40, 0x72, 0x83, 0xfb, 0x51, 0x6, 0x29, 0x54, 0x5, 0x5c, 0x9a, 0xa9, 0xde, 0x7f, 0x9c, 0xc1, 0xd2, 0xce, 0xff, 0xbd, 0xff, 0xa9, 0xa7, 0x30, 0xf0, 0xa0, 0x85, 0x83, 0xa0, 0xa1, 0xc7, 0x3, 0x88, 0x25, 0xae, 0xff, 0x3d, 0x1f, 0x30, 0x69, 0x30, 0x30, 0xa1, 0x2a, 0x60, 0x64, 0x10, 0xb1, 0x99, 0xe3, 0x5, 0xd4, 0xed, 0xfc, 0xdf, 0xeb, 0xbf, 0xd5, 0x54, 0x6, 0x61, 0xcc, 0xa0, 0x66, 0x65, 0xd3, 0xf1, 0xb8, 0xed, 0xf6, 0xdf, 0xed, 0xbf, 0xe7, 0x1d, 0x36, 0x1d, 0x6, 0x56, 0x6c, 0xb1, 0xc1, 0x2d, 0xe3, 0xef, 0xf7, 0xc5, 0xef, 0xbb, 0x42, 0x28, 0xba, 0xfd, 0x48, 0xd6, 0x58, 0x16, 0xdb, 0xd6, 0x2, 0xe3, 0x81, 0x11, 0x57, 0x8c, 0xb2, 0x30, 0x70, 0x3, 0x75, 0xb3, 0x20, 0xb, 0x1, 0x0, 0x4, 0x5c, 0x63, 0x9b, 0x17, 0x86, 0x76, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x1e, 0x30, 0x33, 0xf0, 0x32, 0xc8, 0x30, 0xa8, 0x3, 0xa1, 0xc, 0x90, 0xc5, 0x8c, 0x29, 0x2d, 0x66, 0x9c, 0xe7, 0x7a, 0xd0, 0xe7, 0xbd, 0xcf, 0x7b, 0xd7, 0x83, 0xc6, 0x79, 0xc, 0x62, 0xe8, 0x4a, 0x78, 0x8d, 0xf3, 0x3c, 0x5f, 0xb8, 0xff, 0x77, 0x1, 0x42, 0xf7, 0xff, 0x9e, 0x2f, 0x80, 0x4a, 0x78, 0x51, 0x15, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x81, 0x40, 0x25, 0xae, 0x7, 0x81, 0x16, 0xa1, 0x0, 0x75, 0x9f, 0xf7, 0x2e, 0x30, 0x5, 0x40, 0x53, 0x7c, 0xde, 0x3, 0xdd, 0x82, 0x2, 0x34, 0xbc, 0x3f, 0x20, 0x2b, 0xf0, 0xfe, 0xc0, 0xa0, 0x81, 0xaa, 0x40, 0xc9, 0xe3, 0xb6, 0x2b, 0x92, 0x2, 0xf7, 0x7b, 0xc, 0x2a, 0xa8, 0xa, 0xc4, 0x1d, 0xd7, 0x7b, 0x20, 0xb9, 0xc1, 0xfd, 0x28, 0x83, 0x14, 0xaa, 0x2, 0x2e, 0xcd, 0x54, 0xef, 0x3f, 0xce, 0x60, 0x69, 0xe7, 0xff, 0xde, 0xff, 0xd4, 0x53, 0x18, 0x78, 0xd0, 0xc2, 0x41, 0xd0, 0xd0, 0xe3, 0x1, 0xc4, 0x12, 0xd7, 0xff, 0x9e, 0xf, 0x98, 0x34, 0x18, 0x98, 0x50, 0x15, 0x30, 0x32, 0x88, 0xd8, 0xcc, 0xf1, 0xfa, 0xef, 0xc, 0x84, 0x5e, 0xff, 0xad, 0xa6, 0x32, 0x8, 0x63, 0x6, 0x35, 0x2b, 0x9b, 0x8e, 0xc7, 0x6d, 0xb7, 0xff, 0x6e, 0xff, 0x3d, 0xef, 0xb0, 0xe9, 0x30, 0xb0, 0x62, 0x8b, 0xd, 0x6e, 0x19, 0x7f, 0xbf, 0x2f, 0x7e, 0xdf, 0x15, 0x42, 0x11, 0xf6, 0x63, 0x58, 0x63, 0x59, 0x6c, 0x5b, 0xcb, 0x20, 0x6, 0x64, 0xe1, 0x0, 0x2c, 0xc, 0xdc, 0x40, 0xdd, 0x2c, 0xc8, 0x42, 0x0, 0x4, 0x5c, 0x63, 0x9b, 0xfc, 0xae, 0x1b, 0x2a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hslider_grabber_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xc6, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0x83, 0x83, 0x60, 0xaf, 0xb1, 0x65, 0xbb, 0xca, 0x61, 0xb3, 0xc2, 0x0, 0x0, 0x0, 0x63, 0xb7, 0xc8, 0x63, 0xb7, 0xc7, 0x0, 0x0, 0x0, 0x61, 0xb3, 0xbc, 0x60, 0xb1, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x63, 0xb4, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x69, 0x69, 0x5e, 0xb1, 0xcd, 0x5e, 0xb0, 0xcd, 0x36, 0x63, 0x63, 0x0, 0x0, 0x0, 0x17, 0x2a, 0x29, 0x60, 0xb2, 0xbd, 0x62, 0xb3, 0xbf, 0x3, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x9b, 0x9a, 0x52, 0x96, 0x95, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf, 0xf, 0x62, 0xb4, 0xbd, 0x63, 0xb7, 0xbf, 0x0, 0x0, 0x0, 0x27, 0x48, 0x47, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x2d, 0x52, 0x52, 0x51, 0x93, 0x92, 0x56, 0x9d, 0x9c, 0x0, 0x0, 0x0, 0x54, 0xa2, 0xc8, 0x4c, 0x94, 0xc2, 0x48, 0x8e, 0xc0, 0x47, 0x8c, 0xbf, 0x4b, 0x93, 0xc2, 0x4b, 0x92, 0xc2, 0x4f, 0x98, 0xc4, 0x4d, 0x96, 0xc3, 0x55, 0xa3, 0xc8, 0x53, 0x9f, 0xc7, 0x49, 0x8f, 0xc0, 0x4e, 0x97, 0xc4, 0x5a, 0xab, 0xcb, 0x5a, 0xac, 0xcc, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0xff, 0xff, 0xff, 0x6b, 0x1e, 0xb5, 0x61, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0xd, 0x1c, 0x27, 0x16, 0x6e, 0xc1, 0xef, 0xe8, 0x28, 0xf0, 0xf0, 0x22, 0xdb, 0xde, 0x24, 0x17, 0xaf, 0xc5, 0x1a, 0xa, 0x65, 0xfc, 0xfe, 0x64, 0xc, 0x31, 0xe0, 0xe0, 0x28, 0x2, 0x1, 0x14, 0x9c, 0x95, 0x13, 0x5, 0x2c, 0xdb, 0xdc, 0xb, 0x4f, 0xf4, 0xf7, 0x55, 0x73, 0x7d, 0x4, 0x28, 0xf1, 0xfd, 0xa1, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x41, 0x89, 0xde, 0x6c, 0x4e, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x9e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0xcf, 0xd7, 0x12, 0x82, 0x30, 0x10, 0x5, 0x50, 0x48, 0x42, 0x12, 0xb0, 0x77, 0x8d, 0xd, 0xb, 0x28, 0x56, 0x12, 0x62, 0xd, 0x96, 0xff, 0xff, 0x2a, 0x61, 0xc, 0xe0, 0x83, 0xfb, 0xb4, 0xf7, 0xcc, 0xec, 0xcc, 0x5d, 0xc3, 0xf8, 0x37, 0x26, 0x80, 0x8, 0x41, 0x60, 0xe6, 0xd9, 0xc2, 0x84, 0x52, 0x82, 0xad, 0x4c, 0x0, 0xb6, 0xb9, 0x10, 0xdc, 0xc6, 0x40, 0x3, 0x24, 0x3c, 0x92, 0x32, 0xe2, 0x4, 0x6a, 0x40, 0x54, 0xc8, 0x64, 0x4, 0x45, 0x1a, 0x9c, 0xd2, 0x29, 0x85, 0x73, 0xd9, 0xd1, 0x50, 0xa9, 0x5e, 0x52, 0xb8, 0xd6, 0xea, 0x1a, 0x1a, 0xcd, 0x5b, 0xa, 0xf7, 0x56, 0x5b, 0x43, 0xa7, 0xdb, 0x53, 0x52, 0xaa, 0xfe, 0x80, 0x65, 0x3d, 0x86, 0xa3, 0x58, 0xca, 0x78, 0x3c, 0x99, 0x6a, 0x70, 0x67, 0xf3, 0x87, 0x52, 0xcf, 0xc5, 0x32, 0xaf, 0xee, 0xf9, 0xab, 0xd7, 0x7b, 0xed, 0x7b, 0xc5, 0x33, 0xc1, 0x66, 0xbb, 0xdb, 0xb3, 0x22, 0x27, 0x47, 0x87, 0xa3, 0xe5, 0xfe, 0xfe, 0x1b, 0x6, 0x2c, 0xfc, 0x6e, 0x1f, 0x93, 0x2a, 0x10, 0x62, 0x3, 0x21, 0x32, 0x75, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xc3, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0x83, 0x83, 0x60, 0xaf, 0xb1, 0x65, 0xbb, 0xca, 0x61, 0xb3, 0xc2, 0x0, 0x0, 0x0, 0x63, 0xb7, 0xc8, 0x63, 0xb7, 0xc7, 0x0, 0x0, 0x0, 0x61, 0xb3, 0xbc, 0x60, 0xb1, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x63, 0xb4, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x69, 0x69, 0x5e, 0xb1, 0xcd, 0x5e, 0xb0, 0xcd, 0x36, 0x63, 0x63, 0x0, 0x0, 0x0, 0x17, 0x2a, 0x29, 0x60, 0xb2, 0xbd, 0x62, 0xb3, 0xbf, 0x3, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x9b, 0x9a, 0x52, 0x96, 0x95, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf, 0xf, 0x62, 0xb4, 0xbd, 0x63, 0xb7, 0xbf, 0x0, 0x0, 0x0, 0x27, 0x48, 0x47, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x2d, 0x52, 0x52, 0x51, 0x93, 0x92, 0x56, 0x9d, 0x9c, 0x0, 0x0, 0x0, 0x54, 0xa2, 0xc8, 0x4c, 0x94, 0xc2, 0x48, 0x8e, 0xc0, 0x47, 0x8c, 0xbf, 0x4b, 0x93, 0xc2, 0x4b, 0x92, 0xc2, 0x4f, 0x98, 0xc4, 0x4d, 0x96, 0xc3, 0x55, 0xa3, 0xc8, 0x53, 0x9f, 0xc7, 0x49, 0x8f, 0xc0, 0x4e, 0x97, 0xc4, 0x5a, 0xab, 0xcb, 0x5a, 0xac, 0xcc, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0xd4, 0xd, 0x1d, 0x1c, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0xd, 0x1c, 0x27, 0x16, 0x6e, 0xc1, 0xef, 0xe8, 0x28, 0xf0, 0xf0, 0x22, 0xdb, 0xde, 0x24, 0x17, 0xaf, 0xc5, 0x1a, 0xa, 0x65, 0xfc, 0xfe, 0x64, 0xc, 0x31, 0xe0, 0xe0, 0x28, 0x2, 0x1, 0x14, 0x9c, 0x95, 0x13, 0x5, 0x2c, 0xdb, 0xdc, 0xb, 0x4f, 0xf4, 0xf7, 0x55, 0x73, 0x7d, 0x4, 0x28, 0xf1, 0xfd, 0xa1, 0x0, 0x0, 0x0, 0x7d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5d, 0xcc, 0x81, 0x6, 0xc3, 0x40, 0x10, 0x84, 0xe1, 0xdd, 0x9d, 0xd9, 0x3d, 0x40, 0x1f, 0xa1, 0x0, 0xd1, 0xbe, 0xff, 0xbb, 0x94, 0x10, 0x29, 0x40, 0x1a, 0x40, 0x9, 0xa, 0xbd, 0x34, 0x7b, 0x39, 0xfa, 0x63, 0xf0, 0x61, 0x4c, 0xfe, 0xfa, 0x81, 0x1a, 0x48, 0x98, 0x36, 0x50, 0x66, 0xda, 0x40, 0x71, 0xab, 0x41, 0x3b, 0xc, 0x56, 0x1b, 0x3a, 0xd4, 0x8e, 0x4d, 0x8, 0x40, 0x6a, 0x64, 0x24, 0x0, 0xb6, 0x83, 0xa1, 0x1, 0x79, 0x0, 0x79, 0xc2, 0x54, 0x44, 0xca, 0x14, 0x91, 0xb0, 0xba, 0xef, 0xa7, 0xee, 0x9e, 0x70, 0x8d, 0xd0, 0x52, 0x2c, 0xe2, 0x99, 0x30, 0x93, 0xb, 0x7d, 0x81, 0x4a, 0x82, 0x8c, 0xc0, 0xba, 0xfa, 0x7b, 0x3c, 0x41, 0xb6, 0xd, 0x78, 0x84, 0x74, 0x98, 0x2f, 0x9f, 0xd7, 0x7d, 0x96, 0xbd, 0x2f, 0xa5, 0x6b, 0x13, 0xc4, 0x35, 0x90, 0x18, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hslider_tick_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xc3, 0x98, 0xc3, 0xc0, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x1e, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x38, 0x55, 0x5f, 0x8c, 0xac, 0xb8, 0x81, 0xa2, 0xad, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x8d, 0x8d, 0x8d, 0x82, 0x82, 0x82, 0x4e, 0x4e, 0x4e, 0xff, 0xff, 0xff, 0xc1, 0xc9, 0xb1, 0x80, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x31, 0x79, 0x79, 0x1c, 0x7e, 0xed, 0x4b, 0xf4, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x9, 0xf1, 0xd9, 0xa5, 0xec, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x21, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x9, 0x60, 0x60, 0xb, 0x60, 0x60, 0xf, 0x60, 0x60, 0x6d, 0x60, 0x60, 0x14, 0x60, 0xc0, 0x4, 0x4c, 0x2, 0xc, 0xcc, 0x2, 0xc, 0x30, 0x65, 0x0, 0x46, 0x9d, 0x2, 0xbe, 0x9f, 0x3a, 0x6c, 0xab, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xc3, 0x98, 0xc3, 0xc0, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x38, 0x55, 0x5f, 0x8c, 0xac, 0xb8, 0x81, 0xa2, 0xad, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x8d, 0x8d, 0x8d, 0x82, 0x82, 0x82, 0x4e, 0x4e, 0x4e, 0xc9, 0xf6, 0x7, 0x31, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x31, 0x79, 0x79, 0x1c, 0x7e, 0xed, 0x4b, 0xf4, 0x0, 0x0, 0x0, 0x20, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x9, 0x60, 0x60, 0xb, 0x60, 0x60, 0xf, 0x60, 0x60, 0x6d, 0x60, 0x60, 0x14, 0x60, 0xc0, 0x4, 0x4c, 0x2, 0xc, 0xcc, 0x2, 0x70, 0x65, 0x0, 0x46, 0x9d, 0x2, 0xbe, 0xb7, 0xdf, 0x14, 0x38, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hsplit_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x40, 0x5, 0x0, 0x0, 0x10, 0x0, 0x1, 0xa1, 0xc5, 0x21, 0xc1, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char hsplitter_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x40, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, 0x6f, 0x69, 0x56, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x80, 0x2, 0x66, 0x86, 0x5, 0xa2, 0xe7, 0x18, 0x16, 0x88, 0x9e, 0x63, 0x66, 0x10, 0xbd, 0xf6, 0x98, 0x41, 0xf4, 0xda, 0x63, 0x6, 0xc, 0x30, 0xaa, 0x66, 0x54, 0xd, 0x9a, 0x1a, 0x0, 0x5c, 0x29, 0x6a, 0x81, 0x90, 0x8f, 0x52, 0x12, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x40, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, 0x6f, 0x69, 0x56, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x1a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x80, 0x5, 0xa9, 0x10, 0xcc, 0x90, 0x6a, 0xd, 0xc6, 0x70, 0x80, 0x4b, 0xcd, 0xa8, 0x9a, 0x51, 0x35, 0x0, 0x78, 0xd5, 0x34, 0xa1, 0x54, 0x8c, 0xd5, 0x84, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_add_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0xa0, 0x25, 0x78, 0xf0, 0xe0, 0xc1, 0xff, 0x7, 0xf, 0x1e, 0xfc, 0xc7, 0xa7, 0x86, 0x89, 0x52, 0x4b, 0x46, 0xd, 0x60, 0x60, 0x60, 0x64, 0x60, 0x80, 0x84, 0x36, 0x39, 0x9a, 0x15, 0x14, 0x14, 0x18, 0x29, 0x76, 0x1, 0x5e, 0x30, 0x9a, 0xe, 0xe8, 0x64, 0x0, 0xc5, 0x0, 0x0, 0xc7, 0x6e, 0x12, 0x94, 0xf9, 0x26, 0x2e, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xff, 0xe0, 0xff, 0xd0, 0x52, 0x80, 0x10, 0xc4, 0xd, 0x9, 0x2a, 0x18, 0x26, 0xe1, 0x40, 0x18, 0x0, 0x0, 0x5b, 0x26, 0x61, 0x4d, 0xc9, 0xc1, 0x48, 0x81, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_close_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9b, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x31, 0xe, 0xc2, 0x30, 0x10, 0x4, 0x17, 0xaa, 0x3d, 0x67, 0xdb, 0x58, 0xd0, 0xd3, 0xf0, 0xa3, 0x7c, 0x36, 0x3c, 0x82, 0x48, 0x44, 0x22, 0x6f, 0xb1, 0x4d, 0x85, 0x14, 0x81, 0xf, 0x2c, 0x28, 0xe0, 0xda, 0xd5, 0x8c, 0x4e, 0x77, 0xb, 0xfc, 0xd5, 0x98, 0xd9, 0x20, 0x29, 0x7a, 0xb9, 0xa4, 0x68, 0x66, 0x83, 0xb, 0x93, 0xcc, 0x24, 0xa7, 0x9a, 0x44, 0x52, 0x24, 0x39, 0x91, 0xcc, 0x55, 0x89, 0xa4, 0xde, 0xcc, 0xce, 0x24, 0xb, 0xc9, 0x39, 0x84, 0xb0, 0xf7, 0xb2, 0xae, 0xeb, 0x76, 0xde, 0x8a, 0x4f, 0x92, 0x66, 0xd8, 0x91, 0x5c, 0x49, 0x5e, 0x9a, 0xe1, 0xb5, 0x64, 0x5, 0x16, 0x92, 0x8b, 0x7, 0x6f, 0x9b, 0x8c, 0x0, 0x4a, 0x29, 0x9b, 0x26, 0x81, 0xa4, 0x3e, 0xa5, 0x34, 0x2, 0x38, 0x2, 0x58, 0x0, 0xcc, 0x0, 0xe, 0x39, 0xe7, 0xd3, 0xfa, 0xb0, 0xee, 0xea, 0x8f, 0x7, 0x7b, 0xf5, 0x9d, 0xb7, 0xb0, 0x97, 0x55, 0x25, 0x5f, 0x17, 0xe9, 0x2e, 0xf9, 0xb8, 0xca, 0x3f, 0x9b, 0x1b, 0x1a, 0xe3, 0x40, 0x47, 0xa0, 0xda, 0xda, 0x61, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_color_pick_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x1, 0x55, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xb5, 0x92, 0xb1, 0x4a, 0x3, 0x41, 0x14, 0x45, 0xef, 0xdb, 0x6c, 0xac, 0xf2, 0x1, 0xda, 0xcd, 0x40, 0x4, 0x25, 0x11, 0x4, 0x8b, 0x80, 0x8d, 0x20, 0x88, 0x5d, 0x20, 0x88, 0x90, 0x22, 0x9d, 0x9d, 0xa0, 0x85, 0x58, 0xeb, 0x7, 0x88, 0xe0, 0x7, 0x58, 0x8b, 0x36, 0x5a, 0x88, 0xa0, 0xa5, 0x85, 0xa5, 0x60, 0x61, 0xb0, 0x70, 0xb2, 0xef, 0x8d, 0x44, 0x41, 0x21, 0x1f, 0x20, 0x6c, 0xf2, 0x2c, 0xcc, 0xc2, 0x12, 0x92, 0x8d, 0x45, 0xbc, 0xe5, 0xdc, 0xb9, 0x67, 0xee, 0xbc, 0x19, 0xe0, 0xbf, 0xe5, 0xbd, 0x2f, 0x35, 0x9b, 0xcd, 0x29, 0x0, 0x10, 0x91, 0x9a, 0x88, 0x7c, 0x88, 0xc8, 0xa7, 0x88, 0xd4, 0x0, 0x80, 0xb2, 0xc2, 0xcc, 0x5c, 0x21, 0xa2, 0x5b, 0x22, 0xba, 0xef, 0x76, 0xbb, 0x97, 0x44, 0x74, 0x4a, 0x44, 0x61, 0xdf, 0xfe, 0x32, 0xc6, 0x4c, 0x87, 0x59, 0x0, 0x0, 0xdf, 0x0, 0x62, 0x55, 0xad, 0x6, 0x41, 0x50, 0x1d, 0xf0, 0x62, 0x0, 0x8, 0xb2, 0xd2, 0xd6, 0xda, 0x27, 0x0, 0x27, 0x83, 0xeb, 0xaa, 0x1a, 0x3, 0xd8, 0x19, 0xb, 0x60, 0xe6, 0x3a, 0x80, 0x6d, 0x0, 0xd, 0x0, 0x9d, 0x14, 0x60, 0xcb, 0x18, 0x73, 0x95, 0x9, 0x60, 0xe6, 0x3a, 0x11, 0x1d, 0xc7, 0x71, 0xbc, 0x6e, 0x8c, 0x39, 0x53, 0xd5, 0xdd, 0xc4, 0xcb, 0xe5, 0x72, 0x1b, 0xc9, 0x60, 0x87, 0x2, 0xd2, 0xe1, 0x62, 0xb1, 0xf8, 0xec, 0xbd, 0x2f, 0x11, 0xd1, 0x51, 0xaf, 0xd7, 0x3b, 0x0, 0xd0, 0x51, 0xd5, 0x95, 0x42, 0xa1, 0x30, 0x3b, 0xf2, 0x64, 0x11, 0x79, 0x77, 0xce, 0x95, 0x81, 0xdf, 0x67, 0x14, 0x91, 0x76, 0xff, 0x3a, 0x60, 0xe6, 0x45, 0x66, 0xae, 0xc, 0xd, 0x47, 0x51, 0xb4, 0x29, 0x22, 0xed, 0x56, 0xab, 0xb5, 0xd0, 0xdf, 0x3c, 0xe7, 0xbd, 0x7f, 0x8b, 0xa2, 0xa8, 0x31, 0x7a, 0x52, 0x93, 0x8, 0x8b, 0xc8, 0x72, 0xba, 0xb6, 0x73, 0xae, 0x9c, 0xae, 0x9d, 0xa5, 0x0, 0x0, 0x88, 0x68, 0xd, 0xc0, 0x8b, 0xaa, 0xbe, 0x3a, 0xe7, 0xca, 0x61, 0x18, 0xde, 0xa9, 0xea, 0xbe, 0xb5, 0xf6, 0x7c, 0x1c, 0x20, 0xf9, 0x89, 0x4b, 0x0, 0x56, 0xf3, 0xf9, 0xfc, 0xd, 0x80, 0x79, 0x55, 0xdd, 0xb3, 0xd6, 0x5e, 0x8c, 0xad, 0x9e, 0x0, 0x54, 0x75, 0x86, 0x88, 0xae, 0x1, 0x3c, 0x2, 0x38, 0x34, 0xc6, 0x3c, 0xfc, 0x25, 0x3c, 0x11, 0xfd, 0x0, 0x28, 0x54, 0xbb, 0xfe, 0xad, 0x99, 0xe5, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xaa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x9d, 0x8e, 0x35, 0x82, 0x2, 0x41, 0x10, 0x45, 0x3b, 0xda, 0x3d, 0xca, 0xba, 0x44, 0x2b, 0x70, 0x9, 0xdc, 0xe1, 0x20, 0xe8, 0x91, 0x90, 0x78, 0x6e, 0x40, 0x4c, 0x82, 0x74, 0xff, 0xc2, 0x9d, 0x18, 0xa7, 0x6, 0x77, 0x7b, 0x23, 0x2d, 0xaf, 0x4c, 0xdc, 0xc, 0xbd, 0x65, 0x1e, 0x84, 0x80, 0x19, 0x55, 0x34, 0x60, 0x3e, 0xd0, 0xea, 0x17, 0x3d, 0x4a, 0xc8, 0x80, 0x1a, 0x60, 0xc2, 0x4f, 0xfd, 0x30, 0xe0, 0x1b, 0x2d, 0x16, 0xab, 0xa7, 0x2c, 0xe, 0x41, 0x68, 0xa5, 0xb9, 0xca, 0x91, 0x16, 0x2e, 0x54, 0xe0, 0x59, 0x54, 0x91, 0xfe, 0xa3, 0x3a, 0xff, 0xce, 0xab, 0x5b, 0xf, 0xa0, 0x4, 0x8f, 0x7b, 0x4c, 0xd3, 0x1b, 0xca, 0x32, 0xcc, 0x55, 0x7a, 0xf4, 0x76, 0x42, 0x2b, 0x97, 0x3e, 0xae, 0xfa, 0xdd, 0xd2, 0xd2, 0x8e, 0x72, 0xe1, 0x83, 0xaf, 0x9f, 0xa9, 0x28, 0x7d, 0x5b, 0xe2, 0x2a, 0xd, 0xc3, 0xa2, 0x78, 0xfe, 0x7d, 0x51, 0xfc, 0x0, 0x8a, 0x41, 0xcb, 0x3d, 0xb2, 0xae, 0x1c, 0xd3, 0xc, 0xa5, 0x30, 0x81, 0xc6, 0xda, 0x29, 0x8e, 0x83, 0x34, 0x25, 0x29, 0x4a, 0x46, 0x71, 0x1f, 0x33, 0xbe, 0x51, 0x89, 0xaf, 0x78, 0xe3, 0x97, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_folder_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x8f, 0xc1, 0xd, 0x80, 0x30, 0x8, 0x45, 0x9f, 0x9d, 0x84, 0x39, 0x4c, 0x3b, 0xbd, 0x75, 0x8f, 0x32, 0x9, 0x5e, 0xec, 0xa5, 0x9, 0xa4, 0xc6, 0x26, 0x5e, 0x7c, 0x17, 0xe, 0xc0, 0xe3, 0x3, 0x5f, 0xb3, 0x1, 0xb4, 0xd6, 0x4e, 0x60, 0x77, 0x66, 0xaa, 0x88, 0x14, 0x4f, 0x90, 0xee, 0xea, 0x2d, 0x3, 0xe4, 0x28, 0x41, 0x8a, 0x9a, 0x1d, 0x55, 0x75, 0x25, 0xfd, 0x5, 0x9b, 0x11, 0xd, 0x54, 0x11, 0x29, 0x53, 0x9, 0x1c, 0x32, 0x4c, 0xbe, 0x10, 0xf1, 0xb, 0x16, 0xa, 0xea, 0xd3, 0x45, 0x33, 0x3b, 0xde, 0x1e, 0x5f, 0xc3, 0x5, 0x1f, 0xc5, 0x12, 0x2c, 0xc5, 0x88, 0xe1, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x6, 0x78, 0x70, 0xf4, 0xc1, 0x7f, 0x24, 0x78, 0x18, 0x53, 0xc1, 0x7f, 0x54, 0x48, 0x50, 0xc1, 0x43, 0x1b, 0xbc, 0xa, 0x50, 0xad, 0x23, 0xa4, 0xe0, 0xff, 0x70, 0x52, 0x70, 0x18, 0x97, 0xf4, 0xfd, 0x43, 0xd4, 0x88, 0x4a, 0x0, 0x5a, 0xcb, 0x18, 0xab, 0x5e, 0xd9, 0x1a, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_parent_folder_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0xc6, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xdd, 0x90, 0xbd, 0x6a, 0x2, 0x51, 0x10, 0x46, 0xcf, 0x8c, 0xbb, 0xaf, 0x10, 0xdb, 0xcb, 0x2e, 0x4b, 0x40, 0xf2, 0x14, 0x51, 0x8b, 0x3c, 0x70, 0x44, 0x48, 0x48, 0x15, 0x4c, 0x61, 0x67, 0xd2, 0x4, 0x96, 0xb, 0x5b, 0x89, 0x58, 0x59, 0xee, 0x8f, 0x3b, 0x36, 0xbb, 0x8d, 0x78, 0xa3, 0x92, 0x4a, 0x4f, 0xf9, 0xcd, 0xcc, 0x99, 0x61, 0xe0, 0xe6, 0x91, 0x50, 0xc1, 0x7b, 0x3f, 0x54, 0xd5, 0x39, 0x60, 0x6d, 0xdb, 0xbe, 0x24, 0x49, 0xb2, 0xb9, 0x58, 0x90, 0xe7, 0xf9, 0x43, 0x1c, 0xc7, 0xef, 0x66, 0xf6, 0xd4, 0x45, 0xbf, 0xc0, 0xb3, 0x73, 0x6e, 0x7d, 0x56, 0x70, 0x62, 0xb8, 0xe7, 0xa4, 0x44, 0x8f, 0xcf, 0x8e, 0xa2, 0xe8, 0xa3, 0x1b, 0xfe, 0xee, 0x62, 0x13, 0x91, 0x1f, 0xe0, 0x11, 0x78, 0xf3, 0xde, 0xf, 0x83, 0x2, 0x55, 0x9d, 0x1, 0x23, 0x60, 0xd5, 0x34, 0xcd, 0xb4, 0xcf, 0xab, 0xaa, 0x1a, 0x77, 0xc2, 0x91, 0xaa, 0xbe, 0x6, 0x5, 0xc0, 0xe, 0xf8, 0x2a, 0xcb, 0x72, 0x92, 0xa6, 0xe9, 0xb6, 0xf, 0xb3, 0x2c, 0xdb, 0xd6, 0x75, 0x3d, 0x11, 0x91, 0x25, 0x50, 0xfe, 0xf9, 0x83, 0x1e, 0x33, 0x93, 0xa2, 0x28, 0xf6, 0x80, 0x39, 0xe7, 0x6, 0xa1, 0xbe, 0xe3, 0xb, 0xae, 0x26, 0x28, 0x10, 0x11, 0x3, 0x16, 0x22, 0xf2, 0xf9, 0xdf, 0x25, 0xf7, 0xce, 0x1, 0x9e, 0x13, 0x48, 0xe9, 0x87, 0xc5, 0x3a, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x68, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x33, 0xb8, 0x27, 0xfe, 0xe0, 0xfc, 0x83, 0x73, 0xf7, 0xc4, 0x71, 0x48, 0xdf, 0x11, 0x7b, 0x78, 0xe9, 0xc1, 0x3f, 0x20, 0xbc, 0xfe, 0x40, 0x12, 0x8f, 0x34, 0x4c, 0x9, 0xa6, 0xe1, 0x57, 0x80, 0x12, 0x17, 0x81, 0xf8, 0x2f, 0x58, 0xe1, 0x15, 0x34, 0x8b, 0x1e, 0x9c, 0x5, 0xa, 0x5e, 0xb8, 0x23, 0x6, 0x52, 0x70, 0x5b, 0x14, 0xac, 0xf0, 0xc, 0xaa, 0x82, 0x7d, 0xf, 0x8e, 0xde, 0x14, 0xf9, 0xcf, 0x8, 0x52, 0xc0, 0xc0, 0x70, 0x5b, 0xf4, 0xe1, 0xc9, 0x7, 0x47, 0xb1, 0xb8, 0x3, 0xaa, 0x0, 0xa, 0x48, 0x52, 0x80, 0xb0, 0xea, 0xc8, 0xc3, 0x83, 0xc, 0x83, 0xe, 0x0, 0x0, 0xb8, 0x27, 0x55, 0x4c, 0xbe, 0xc0, 0xd2, 0xac, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_play_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xa2, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0x18, 0xf2, 0x80, 0x11, 0x99, 0xf3, 0xe0, 0xc1, 0x83, 0xc3, 0xc, 0xc, 0xc, 0xc2, 0xc, 0xc, 0xc, 0xa5, 0xa, 0xa, 0xa, 0x5b, 0xc9, 0x31, 0xe0, 0x3f, 0x5c, 0x82, 0x91, 0x71, 0x27, 0x3, 0x3, 0x43, 0x91, 0xbc, 0xbc, 0xfc, 0x35, 0x7c, 0x6, 0x30, 0xe1, 0x92, 0xf8, 0xff, 0xff, 0xbf, 0xfb, 0xff, 0xff, 0xff, 0x2f, 0x3e, 0x78, 0xf0, 0x60, 0xca, 0x93, 0x27, 0x4f, 0x84, 0x49, 0x76, 0x1, 0x1a, 0xf8, 0xc0, 0xc8, 0xc8, 0xd8, 0xf1, 0xeb, 0xd7, 0xaf, 0x9, 0xaa, 0xaa, 0xaa, 0x3f, 0x89, 0x72, 0x1, 0x1a, 0x10, 0xf8, 0xff, 0xff, 0x7f, 0x7, 0x2b, 0x2b, 0xeb, 0x1e, 0x74, 0x9, 0x62, 0xd, 0xc0, 0x9, 0x88, 0x35, 0xe0, 0x3d, 0x23, 0x23, 0x63, 0xc5, 0xef, 0xdf, 0xbf, 0x5d, 0xd0, 0x25, 0x58, 0x8, 0x68, 0xfc, 0xc3, 0xc0, 0xc0, 0x30, 0x93, 0x85, 0x85, 0xa5, 0x5e, 0x46, 0x46, 0xe6, 0x2d, 0x36, 0x5, 0x38, 0xd, 0x20, 0x36, 0x1a, 0xd1, 0xd, 0x38, 0xc2, 0x0, 0x4d, 0x48, 0xf2, 0xf2, 0xf2, 0x44, 0x25, 0xa4, 0x61, 0x0, 0x0, 0x1e, 0x57, 0x33, 0x3c, 0xcc, 0xe7, 0x34, 0x69, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x41, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x1b, 0x78, 0x70, 0xf8, 0xc1, 0xb5, 0x7, 0xde, 0xf8, 0x14, 0xfc, 0x7, 0xc1, 0x87, 0x3b, 0x1e, 0x6a, 0xe1, 0x54, 0x0, 0x85, 0xbf, 0x1f, 0x4c, 0x79, 0x22, 0x8c, 0x5d, 0x1, 0x2, 0xbe, 0x7f, 0x58, 0x7e, 0x9b, 0x1d, 0x43, 0x1, 0x1a, 0x3c, 0x4c, 0x91, 0x82, 0x77, 0x8, 0x2b, 0x8, 0x3b, 0x12, 0xd3, 0x9b, 0x84, 0x3, 0x8a, 0xfe, 0x0, 0x0, 0xa4, 0x15, 0x70, 0xca, 0x48, 0x40, 0x6f, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_reload_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x1, 0x59, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x31, 0x4b, 0x3, 0x31, 0x18, 0x86, 0x9f, 0x5c, 0xe, 0xec, 0x20, 0x76, 0x70, 0x39, 0xd0, 0xe3, 0xba, 0x74, 0x51, 0x41, 0xdd, 0xfc, 0x7, 0xba, 0xb8, 0x8, 0x52, 0xec, 0x2e, 0x4e, 0x3a, 0x88, 0x3f, 0xa3, 0xa3, 0x53, 0x7, 0x47, 0x67, 0x41, 0x17, 0xdd, 0x9c, 0x5c, 0x3b, 0xd8, 0x82, 0x64, 0x68, 0xca, 0xd5, 0x55, 0x11, 0x3c, 0x1a, 0x87, 0x24, 0x2e, 0xd7, 0x72, 0x9e, 0x9e, 0xab, 0xbe, 0xd3, 0xc7, 0x1b, 0x9e, 0x37, 0xf9, 0xbe, 0x7c, 0xf0, 0xd7, 0x12, 0x3f, 0x99, 0x4a, 0xa9, 0xb9, 0x30, 0xc, 0x8f, 0x84, 0x10, 0x7, 0xc0, 0x5a, 0x6e, 0x3f, 0x3a, 0xe7, 0x2e, 0xad, 0xb5, 0xdd, 0x66, 0xb3, 0xf9, 0x51, 0x19, 0x90, 0xa6, 0xe9, 0x92, 0xb5, 0xf6, 0x6, 0x58, 0xaf, 0xb8, 0xb4, 0x27, 0xa5, 0xdc, 0x8d, 0xe3, 0xf8, 0x19, 0x20, 0x28, 0xdf, 0x5c, 0x80, 0x53, 0x60, 0xdf, 0x18, 0x53, 0x37, 0xc6, 0xd4, 0xbd, 0xf7, 0x7b, 0xc0, 0x13, 0xb0, 0x61, 0xad, 0xbd, 0x56, 0x4a, 0xcd, 0x1, 0x84, 0xc5, 0x80, 0x30, 0xc, 0x8f, 0xa6, 0xb0, 0x94, 0x72, 0x33, 0x8e, 0xe3, 0x97, 0xc2, 0xf1, 0xd5, 0x68, 0x34, 0xba, 0xf7, 0xde, 0xf7, 0x80, 0xd, 0x29, 0xe5, 0x21, 0x70, 0xfe, 0xe5, 0x5, 0x42, 0x88, 0x76, 0x5e, 0x9e, 0x96, 0x60, 0x0, 0x92, 0x24, 0x79, 0x75, 0xce, 0x9d, 0x1, 0x4, 0x41, 0xd0, 0xfe, 0xd6, 0x2, 0xb0, 0x2, 0x60, 0x8c, 0xb9, 0xab, 0xe8, 0x1f, 0x6b, 0xed, 0x6d, 0x5e, 0xae, 0xce, 0x2, 0xb4, 0xd6, 0x17, 0x5a, 0x6b, 0x7, 0xcc, 0x3, 0xd4, 0x6a, 0xb5, 0x37, 0xad, 0xf5, 0xc5, 0x4f, 0x1, 0x41, 0x10, 0x7c, 0x19, 0x7c, 0x90, 0x3, 0xc7, 0xc0, 0xa0, 0xe0, 0xf, 0x72, 0xef, 0x9b, 0x84, 0x10, 0xdb, 0x79, 0xd9, 0x9f, 0x5, 0x44, 0x51, 0x94, 0x9, 0x21, 0x5a, 0x40, 0x6, 0x64, 0x42, 0x88, 0x56, 0x14, 0x45, 0x59, 0x19, 0x1e, 0x8f, 0xc7, 0x8b, 0x52, 0xca, 0xe, 0x80, 0x73, 0xee, 0x12, 0xa, 0xbf, 0x90, 0x24, 0x49, 0x7f, 0x38, 0x1c, 0x1e, 0x3, 0x34, 0x1a, 0x8d, 0x7e, 0x11, 0x54, 0x4a, 0x2d, 0x48, 0x29, 0x77, 0xac, 0xb5, 0x1d, 0xef, 0xfd, 0x32, 0xd0, 0x9b, 0x4c, 0x26, 0x5d, 0xa8, 0xd8, 0xc4, 0xb2, 0xf2, 0xf9, 0x4c, 0x55, 0xbd, 0x48, 0xbf, 0xe8, 0x1d, 0x78, 0x70, 0xce, 0x9d, 0x64, 0x59, 0xb6, 0x35, 0x85, 0xff, 0x87, 0x3e, 0x1, 0x53, 0x7, 0x87, 0x11, 0xd3, 0x3a, 0x9b, 0x9e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb1, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x50, 0x35, 0xba, 0xc2, 0x40, 0x10, 0x7e, 0xcd, 0x33, 0x5c, 0xca, 0x1c, 0x21, 0x87, 0xc1, 0xa9, 0xd3, 0x23, 0x47, 0xca, 0x69, 0xb0, 0x6, 0x77, 0xdb, 0xfd, 0x17, 0x2f, 0x91, 0x3e, 0xee, 0xd2, 0xc1, 0x4e, 0xb5, 0xdf, 0xcc, 0xaf, 0x5f, 0x9f, 0x7c, 0xdb, 0x5f, 0xda, 0x44, 0x17, 0x2f, 0x75, 0xba, 0xa4, 0xb1, 0xfd, 0xf5, 0xad, 0x8f, 0x1c, 0x86, 0x90, 0x5c, 0x33, 0x38, 0x72, 0x1e, 0xb4, 0xbe, 0x66, 0x28, 0xad, 0xe2, 0xab, 0x38, 0xcd, 0x63, 0xa9, 0x9d, 0xb8, 0x58, 0x68, 0x53, 0x5b, 0x1f, 0x33, 0xd6, 0x9f, 0xa5, 0xc1, 0x20, 0x91, 0xba, 0x7d, 0x80, 0x1e, 0x24, 0x94, 0xdc, 0x92, 0xa4, 0x2, 0x9, 0x1d, 0xe7, 0xe0, 0x9, 0x69, 0x15, 0xf7, 0x58, 0x4e, 0x40, 0xc2, 0xc3, 0x58, 0x8a, 0xb6, 0x31, 0xd1, 0x39, 0xd8, 0x27, 0xed, 0x83, 0x5b, 0x14, 0x33, 0x7d, 0x3d, 0xbb, 0x45, 0x5d, 0x12, 0x55, 0x97, 0x4, 0xe3, 0xb5, 0xf4, 0x8c, 0x77, 0xd6, 0xa7, 0x2c, 0x3b, 0x78, 0x4c, 0x52, 0x81, 0xa, 0x8e, 0x3a, 0xa9, 0x6a, 0x6b, 0xc, 0xe6, 0x3f, 0xa1, 0x8d, 0x86, 0x16, 0xe5, 0x39, 0x78, 0xa2, 0x4d, 0xea, 0xe, 0xfa, 0xdd, 0xa7, 0x0, 0x90, 0x4f, 0x8b, 0xd0, 0xe1, 0x9e, 0x1b, 0xc2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_snap_grid_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0xc2, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xad, 0x90, 0xbd, 0xa, 0xc2, 0x30, 0x14, 0x85, 0xbf, 0x5b, 0x5c, 0x23, 0xe8, 0x6c, 0x9f, 0xc1, 0xb7, 0xd0, 0x47, 0xd1, 0x47, 0x70, 0x48, 0xa1, 0x43, 0x57, 0xe9, 0xd3, 0x88, 0x93, 0xef, 0xe0, 0xea, 0x5c, 0x9d, 0x1d, 0xb2, 0x96, 0xc6, 0xa1, 0x9, 0xd4, 0xd8, 0xd8, 0x4a, 0xfd, 0x20, 0x10, 0xee, 0xcf, 0xe1, 0xdc, 0x3, 0x1d, 0x8c, 0x31, 0xd6, 0x18, 0x63, 0x9, 0x88, 0xd5, 0x1, 0x92, 0xbe, 0xe2, 0x2f, 0x4c, 0x16, 0x90, 0x98, 0xb5, 0x21, 0x94, 0x52, 0xf2, 0x17, 0x7, 0x6f, 0x7c, 0xb, 0x2b, 0xc6, 0x64, 0x7, 0xb3, 0xa1, 0x1, 0x5b, 0x14, 0x29, 0x50, 0x2, 0x1b, 0x0, 0x44, 0x2e, 0x34, 0xcd, 0x41, 0xb2, 0xec, 0x6, 0x20, 0xdd, 0x61, 0x6f, 0xdf, 0x7, 0xe4, 0x96, 0xaf, 0xc0, 0x32, 0xd0, 0x7d, 0x2, 0x6b, 0xd1, 0xba, 0x4a, 0xfc, 0xdd, 0x91, 0xdb, 0x4b, 0xb7, 0x7c, 0x2, 0x56, 0xd4, 0x75, 0xa, 0x9c, 0x81, 0x5, 0x70, 0x84, 0xe1, 0xc, 0x5a, 0xdb, 0x75, 0xbd, 0x17, 0xad, 0x1f, 0x92, 0xe7, 0x77, 0x60, 0xe7, 0x7a, 0x5b, 0x80, 0x44, 0x29, 0x25, 0xfe, 0xf5, 0x8, 0x28, 0x0, 0xb7, 0xd8, 0x46, 0xa0, 0x75, 0xe5, 0xbe, 0xf3, 0x31, 0xe, 0x7e, 0x23, 0xcc, 0xc2, 0x3a, 0xc2, 0xb9, 0x58, 0xfd, 0x83, 0xc9, 0x2, 0x63, 0x78, 0x1, 0x4a, 0x50, 0x70, 0x86, 0xcc, 0x86, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xf3, 0xf3, 0xf3, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x85, 0x85, 0xff, 0x83, 0x83, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x80, 0x80, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0x84, 0x84, 0xff, 0xff, 0xff, 0xa, 0xa5, 0x43, 0x1, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xff, 0x1d, 0xac, 0xf2, 0xaf, 0x27, 0xed, 0xff, 0xee, 0xb4, 0x1b, 0x1c, 0xb6, 0xaa, 0xf1, 0x50, 0xa6, 0xdd, 0x5f, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x2, 0x40, 0x23, 0xd3, 0x60, 0x0, 0x40, 0xc, 0x3, 0xaf, 0x76, 0x93, 0xfd, 0x97, 0x7d, 0x9b, 0x55, 0x70, 0x12, 0x62, 0xaf, 0x68, 0x0, 0xc4, 0xe9, 0x3c, 0x1, 0x67, 0xf7, 0x17, 0x20, 0x95, 0xd6, 0xc6, 0xee, 0x80, 0x74, 0xde, 0x7b, 0x1f, 0x24, 0xb0, 0x64, 0x29, 0x1f, 0x53, 0x2e, 0xbe, 0x6e, 0x80, 0xf6, 0x19, 0x90, 0x9e, 0x36, 0x8b, 0xf7, 0xc0, 0x5c, 0xdf, 0x0, 0x66, 0x60, 0xae, 0xf3, 0xb9, 0x1, 0xfb, 0xe9, 0x1, 0xa6, 0x26, 0x1, 0xcd, 0x30, 0x66, 0x63, 0x6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_stop_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x2b, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x20, 0xc, 0x3e, 0x30, 0xe3, 0x93, 0x61, 0x64, 0x60, 0x60, 0x60, 0x78, 0xf8, 0x1f, 0xbb, 0x2, 0x79, 0x46, 0x6, 0x6, 0x26, 0x42, 0xe6, 0x8f, 0x2a, 0xa0, 0x96, 0x2, 0x6, 0x42, 0x91, 0x45, 0x10, 0x0, 0x0, 0x95, 0x31, 0x5, 0xe4, 0xe, 0x1, 0x8e, 0x3, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x2, 0x7c, 0x60, 0x26, 0x28, 0xf3, 0xf0, 0x3f, 0x76, 0x8, 0x94, 0xa2, 0x97, 0x82, 0x51, 0x5, 0x84, 0x23, 0x8b, 0x30, 0x0, 0x0, 0x66, 0x60, 0x11, 0xdc, 0x92, 0xb3, 0xb7, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_zoom_less_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x25, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0x18, 0x5, 0xc3, 0x0, 0x30, 0x32, 0x30, 0x30, 0x30, 0x3c, 0x78, 0xf0, 0xe0, 0x3f, 0x39, 0x9a, 0x15, 0x14, 0x14, 0x18, 0x99, 0xa8, 0xeb, 0x9e, 0x51, 0x30, 0x44, 0x1, 0x0, 0xff, 0xa9, 0x4, 0x4, 0x4a, 0xae, 0x20, 0xfa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x31, 0xe0, 0xc1, 0x7f, 0x3c, 0x90, 0xb0, 0x82, 0x11, 0x2, 0x0, 0xbf, 0x57, 0x36, 0x25, 0x52, 0x24, 0x7b, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_zoom_more_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0xa0, 0x25, 0x78, 0xf0, 0xe0, 0xc1, 0xff, 0x7, 0xf, 0x1e, 0xfc, 0xc7, 0xa7, 0x86, 0x89, 0x52, 0x4b, 0x46, 0xd, 0x60, 0x60, 0x60, 0x64, 0x60, 0x80, 0x84, 0x36, 0x39, 0x9a, 0x15, 0x14, 0x14, 0x18, 0x29, 0x76, 0x1, 0x5e, 0x30, 0x9a, 0xe, 0xe8, 0x64, 0x0, 0xc5, 0x0, 0x0, 0xc7, 0x6e, 0x12, 0x94, 0xf9, 0x26, 0x2e, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x3c, 0xf8, 0xf, 0x84, 0x43, 0x4a, 0x1, 0x42, 0x10, 0xf, 0x24, 0xa8, 0x60, 0x78, 0x84, 0x3, 0x61, 0x0, 0x0, 0xca, 0x3a, 0x6d, 0x8d, 0x50, 0x1e, 0x9a, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char icon_zoom_reset_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x6f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xdd, 0x91, 0xbb, 0xd, 0x80, 0x30, 0x10, 0x43, 0x1f, 0x11, 0x23, 0x20, 0xea, 0x4c, 0xc5, 0x62, 0x48, 0xec, 0xc0, 0x56, 0x97, 0x26, 0x43, 0xa4, 0x31, 0x15, 0x12, 0x45, 0x72, 0x8a, 0x14, 0xa, 0x84, 0xab, 0xfb, 0x58, 0x3e, 0xeb, 0xc, 0x83, 0x98, 0xee, 0x22, 0xa5, 0xb4, 0x49, 0x3a, 0x1, 0x62, 0x8c, 0x53, 0x8d, 0x5c, 0xe3, 0x4, 0x80, 0x9c, 0xf3, 0x22, 0x69, 0xf7, 0x2e, 0xb5, 0x38, 0x1, 0xa0, 0x94, 0x72, 0x0, 0xab, 0x27, 0xd0, 0xc3, 0xc1, 0xcc, 0x64, 0x66, 0x6a, 0xf5, 0xb5, 0x59, 0x70, 0x15, 0x3b, 0x30, 0x7b, 0xcb, 0xd6, 0x33, 0x9f, 0x18, 0x76, 0xf0, 0x71, 0x81, 0x5a, 0xa, 0xaf, 0x3b, 0xf8, 0x41, 0xa, 0xc3, 0xb8, 0x0, 0x6c, 0x9c, 0x3f, 0xb8, 0x84, 0xfc, 0x5b, 0x85, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0xa, 0x3c, 0xc, 0x7b, 0xf0, 0xff, 0xc1, 0x7f, 0x9c, 0x22, 0xcf, 0x44, 0x1e, 0xbc, 0x84, 0x72, 0xb1, 0x8b, 0x3c, 0x58, 0x5, 0xe4, 0x40, 0xb8, 0x38, 0x45, 0x18, 0x60, 0x5c, 0x84, 0x30, 0x59, 0xa, 0xa0, 0x80, 0x6e, 0xa, 0x86, 0x92, 0x2f, 0x8, 0x3, 0x0, 0x69, 0xc8, 0x86, 0x87, 0x72, 0xca, 0x85, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char line_edit_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xd, 0xf6, 0xb4, 0x61, 0xf5, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + +static const unsigned char line_edit_clear_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char line_edit_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x95, 0xce, 0x31, 0x12, 0x2, 0x21, 0x10, 0x44, 0xd1, 0x3f, 0x40, 0xa1, 0x44, 0xa6, 0x46, 0xde, 0x63, 0x4f, 0xe5, 0x15, 0x38, 0xb2, 0xd6, 0x6, 0xb0, 0xc8, 0x30, 0x6, 0x96, 0xac, 0x56, 0x99, 0xf8, 0xb3, 0x7e, 0x51, 0xcb, 0xf9, 0x1a, 0xb3, 0x3f, 0xa, 0xaf, 0xc, 0xad, 0x2d, 0xcb, 0xe5, 0x76, 0x38, 0x5, 0x76, 0xec, 0x6c, 0xf7, 0xe0, 0x53, 0xe0, 0x13, 0xa1, 0x27, 0x27, 0x43, 0x26, 0x81, 0x20, 0xc8, 0x70, 0xfc, 0xe8, 0xf, 0x34, 0x67, 0xd8, 0x9c, 0x86, 0x61, 0x2e, 0x68, 0xe9, 0x91, 0xaf, 0x4b, 0x5a, 0x7d, 0x2a, 0x2c, 0x3, 0xed, 0xef, 0x1e, 0x6b, 0xcb, 0x4f, 0xa6, 0x66, 0x2b, 0x25, 0x6, 0x1, 0x37, 0x40, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char line_edit_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xab, 0x50, 0x4c, 0x54, 0x45, 0x1b, 0x17, 0x18, 0x1b, 0x17, 0x18, 0x1b, 0x17, 0x18, 0xc8, 0x68, 0x12, 0xef, 0xed, 0xe7, 0xef, 0xed, 0xe8, 0xf0, 0xed, 0xe8, 0xf0, 0xee, 0xe8, 0xf0, 0xed, 0xe7, 0xed, 0xeb, 0xe5, 0xee, 0xeb, 0xe5, 0xee, 0xeb, 0xe6, 0xec, 0xe9, 0xe3, 0xeb, 0xe9, 0xe3, 0xeb, 0xe9, 0xe2, 0xec, 0xe9, 0xe2, 0xe9, 0xe6, 0xe0, 0xea, 0xe7, 0xe0, 0xea, 0xe7, 0xe1, 0xe8, 0xe4, 0xdd, 0xe8, 0xe5, 0xde, 0xe8, 0xe5, 0xdd, 0xe8, 0xe4, 0xde, 0xe6, 0xe2, 0xdb, 0xe6, 0xe3, 0xdb, 0xe6, 0xe3, 0xdc, 0xe7, 0xe2, 0xdb, 0xe7, 0xe3, 0xdb, 0xe4, 0xe0, 0xd8, 0xe5, 0xe0, 0xd8, 0xe5, 0xe1, 0xd9, 0xe5, 0xe0, 0xd9, 0xe4, 0xe1, 0xd9, 0xe5, 0xe1, 0xd8, 0xe4, 0xe0, 0xd9, 0xe2, 0xdf, 0xd6, 0xe3, 0xdf, 0xd6, 0xe3, 0xde, 0xd6, 0xe2, 0xde, 0xd6, 0xe1, 0xdc, 0xd4, 0xe1, 0xdc, 0xd3, 0xe0, 0xdc, 0xd3, 0xe1, 0xdd, 0xd3, 0xe1, 0xdd, 0xd4, 0xdf, 0xda, 0xd0, 0xdf, 0xda, 0xd1, 0xdf, 0xdb, 0xd1, 0xe0, 0xda, 0xd1, 0xdd, 0xd8, 0xcf, 0xdd, 0xd8, 0xce, 0xde, 0xd9, 0xce, 0xde, 0xd8, 0xce, 0xdd, 0xd9, 0xce, 0xdc, 0xd6, 0xcc, 0xdb, 0xd6, 0xcc, 0xdc, 0xd6, 0xcb, 0xff, 0xff, 0xff, 0x73, 0x72, 0x65, 0x6f, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x36, 0x61, 0xc5, 0x3a, 0xd, 0x83, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x38, 0xa0, 0x7, 0xa5, 0xd6, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x9e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x5d, 0xcf, 0xdb, 0x16, 0x82, 0x20, 0x10, 0x85, 0x61, 0xb4, 0xb4, 0x13, 0x1d, 0x28, 0x14, 0xcb, 0x4a, 0x33, 0xf, 0x59, 0x52, 0x98, 0x62, 0xef, 0xff, 0x66, 0xd1, 0xf6, 0xca, 0xfe, 0x19, 0xd6, 0x62, 0x7d, 0x77, 0x43, 0x88, 0x3d, 0x88, 0x10, 0x7b, 0x34, 0xc8, 0x36, 0x30, 0x76, 0x1c, 0x77, 0xe2, 0x3a, 0xee, 0xd4, 0x7c, 0x0, 0xb3, 0x79, 0x3f, 0xa6, 0x5, 0x80, 0xa2, 0x25, 0xa5, 0xab, 0x35, 0x5, 0x6c, 0x18, 0x63, 0x5b, 0xd6, 0x7, 0xd8, 0x71, 0xcf, 0xe3, 0x9c, 0xfb, 0x9c, 0x7b, 0x3e, 0x40, 0x4, 0xfb, 0xe0, 0x10, 0x84, 0xa1, 0x8, 0x85, 0x0, 0x1c, 0x4f, 0xe7, 0x28, 0xbe, 0x44, 0x71, 0x9c, 0x24, 0x67, 0xc0, 0x35, 0x4d, 0xb3, 0x2c, 0xcb, 0xcd, 0x4b, 0x73, 0x40, 0x71, 0x33, 0x15, 0xe5, 0xfd, 0x51, 0x16, 0x25, 0xa0, 0x92, 0xf2, 0xf9, 0x92, 0xbf, 0x2a, 0x9, 0x50, 0xaa, 0x56, 0xef, 0x46, 0xb5, 0x4d, 0x5b, 0x37, 0x0, 0xdd, 0x7d, 0xb4, 0x36, 0xdb, 0x69, 0xad, 0x1, 0x7f, 0xc7, 0x59, 0xc3, 0xf3, 0xad, 0x2f, 0x30, 0x4f, 0x11, 0x50, 0x3e, 0x12, 0x48, 0xdb, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa8, 0x50, 0x4c, 0x54, 0x45, 0x1b, 0x17, 0x18, 0x1b, 0x17, 0x18, 0x1b, 0x17, 0x18, 0xc8, 0x68, 0x12, 0xef, 0xed, 0xe7, 0xef, 0xed, 0xe8, 0xf0, 0xed, 0xe8, 0xf0, 0xee, 0xe8, 0xf0, 0xed, 0xe7, 0xed, 0xeb, 0xe5, 0xee, 0xeb, 0xe5, 0xee, 0xeb, 0xe6, 0xec, 0xe9, 0xe3, 0xeb, 0xe9, 0xe3, 0xeb, 0xe9, 0xe2, 0xec, 0xe9, 0xe2, 0xe9, 0xe6, 0xe0, 0xea, 0xe7, 0xe0, 0xea, 0xe7, 0xe1, 0xe8, 0xe4, 0xdd, 0xe8, 0xe5, 0xde, 0xe8, 0xe5, 0xdd, 0xe8, 0xe4, 0xde, 0xe6, 0xe2, 0xdb, 0xe6, 0xe3, 0xdb, 0xe6, 0xe3, 0xdc, 0xe7, 0xe2, 0xdb, 0xe7, 0xe3, 0xdb, 0xe4, 0xe0, 0xd8, 0xe5, 0xe0, 0xd8, 0xe5, 0xe1, 0xd9, 0xe5, 0xe0, 0xd9, 0xe4, 0xe1, 0xd9, 0xe5, 0xe1, 0xd8, 0xe4, 0xe0, 0xd9, 0xe2, 0xdf, 0xd6, 0xe3, 0xdf, 0xd6, 0xe3, 0xde, 0xd6, 0xe2, 0xde, 0xd6, 0xe1, 0xdc, 0xd4, 0xe1, 0xdc, 0xd3, 0xe0, 0xdc, 0xd3, 0xe1, 0xdd, 0xd3, 0xe1, 0xdd, 0xd4, 0xdf, 0xda, 0xd0, 0xdf, 0xda, 0xd1, 0xdf, 0xdb, 0xd1, 0xe0, 0xda, 0xd1, 0xdd, 0xd8, 0xcf, 0xdd, 0xd8, 0xce, 0xde, 0xd9, 0xce, 0xde, 0xd8, 0xce, 0xdd, 0xd9, 0xce, 0xdc, 0xd6, 0xcc, 0xdb, 0xd6, 0xcc, 0xdc, 0xd6, 0xcb, 0xbd, 0x92, 0xbc, 0xa2, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x36, 0x61, 0xc5, 0x3a, 0xd, 0x83, 0x0, 0x0, 0x0, 0x72, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x4c, 0x8c, 0x55, 0x2, 0x4, 0x1, 0x8, 0x85, 0xc0, 0xc9, 0xfb, 0x1f, 0x76, 0x7b, 0x75, 0x3a, 0xf0, 0xc7, 0xc0, 0x27, 0xc1, 0x9d, 0x34, 0xe4, 0x4e, 0xb5, 0xa0, 0x68, 0x95, 0xf1, 0x93, 0xc4, 0xb0, 0xb7, 0xd6, 0x2, 0x7c, 0x2d, 0x46, 0xc3, 0x82, 0x45, 0xf8, 0x87, 0x16, 0x5a, 0xd7, 0x71, 0x21, 0x9e, 0x2c, 0x86, 0x52, 0x10, 0x89, 0xee, 0x86, 0x15, 0xd9, 0xf0, 0x6b, 0x7f, 0xac, 0x8b, 0xce, 0x85, 0xb4, 0x8b, 0xe1, 0xb7, 0x2e, 0x6, 0xd9, 0x53, 0x6a, 0x3c, 0x43, 0xa9, 0xd0, 0xcc, 0xd8, 0x5f, 0xd0, 0x4, 0xa2, 0xf6, 0x50, 0xac, 0x68, 0xff, 0x6d, 0xf9, 0x3f, 0xc, 0x93, 0x88, 0x59, 0x68, 0x81, 0x69, 0x18, 0x9e, 0x3, 0xba, 0x1b, 0x55, 0x0, 0x0, 0x0, 0x69, 0x26, 0x8d, 0xeb, 0x4b, 0xad, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char logo_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc3, 0x3e, 0x61, 0xcb, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0x2e, 0x23, 0x0, 0x0, 0x2e, 0x23, 0x1, 0x78, 0xa5, 0x3f, 0x76, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xdc, 0x9, 0x7, 0x13, 0x2, 0x0, 0x15, 0xb9, 0x53, 0x97, 0x0, 0x0, 0x21, 0x58, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x7d, 0x79, 0x9c, 0x1c, 0x55, 0xb5, 0xff, 0xf7, 0x7b, 0xab, 0x7b, 0x66, 0x48, 0x8, 0x61, 0xd1, 0x90, 0xb0, 0x88, 0x91, 0x27, 0x24, 0x24, 0x21, 0x3b, 0x8, 0x4, 0x42, 0x58, 0x54, 0xe0, 0x91, 0x80, 0x24, 0x1, 0x2, 0x9, 0x42, 0x80, 0x4, 0x11, 0x90, 0x9f, 0xf2, 0xd3, 0xa7, 0xcf, 0xe7, 0xf3, 0x3d, 0x1f, 0x1f, 0x7f, 0xea, 0xd3, 0x48, 0x4c, 0x8, 0x61, 0xd, 0x49, 0x64, 0x89, 0x82, 0x40, 0x58, 0x4, 0x64, 0xb, 0x44, 0x25, 0x9b, 0xd9, 0x51, 0x14, 0x41, 0x3, 0xc8, 0x16, 0x20, 0xfb, 0x4c, 0x77, 0xdd, 0xef, 0xef, 0x8f, 0xaa, 0xea, 0xae, 0xee, 0xa9, 0xea, 0x9e, 0xee, 0xae, 0x99, 0xe9, 0xe0, 0x9c, 0xf9, 0xd4, 0x74, 0xd5, 0xad, 0x5b, 0x77, 0x39, 0xe7, 0xdc, 0x7b, 0xcf, 0x39, 0xf7, 0xdc, 0x7b, 0x81, 0x2e, 0xe8, 0x82, 0x2e, 0xe8, 0x82, 0x2e, 0xe8, 0x82, 0x2e, 0xe8, 0x82, 0x2e, 0xe8, 0x82, 0x2e, 0xf8, 0xe7, 0x2, 0x76, 0x76, 0x1, 0xea, 0x1, 0xe, 0xff, 0xc2, 0xcc, 0xbd, 0x60, 0x5b, 0x6e, 0xb6, 0xd6, 0xf6, 0x2, 0xb8, 0x8d, 0xc6, 0x6c, 0x36, 0x34, 0x6f, 0x81, 0x7c, 0x43, 0x30, 0x7f, 0x27, 0xcd, 0xdf, 0x8c, 0xe3, 0x6c, 0xda, 0x70, 0xef, 0xf4, 0x37, 0x3b, 0xbb, 0xac, 0x49, 0x43, 0xaa, 0xb3, 0xb, 0x50, 0xf, 0x20, 0x9b, 0xfd, 0x96, 0xcd, 0xb6, 0x4c, 0x40, 0xd0, 0x20, 0x2c, 0xac, 0x4b, 0xb3, 0x3, 0xc0, 0x36, 0x80, 0x5b, 0x41, 0x6c, 0x75, 0x5b, 0xb8, 0xed, 0xb0, 0x71, 0xff, 0xbb, 0x83, 0xc6, 0x6c, 0x26, 0x9d, 0x65, 0x74, 0xd2, 0x4f, 0x6e, 0x5c, 0x74, 0xc5, 0xba, 0xce, 0x2e, 0x7b, 0xad, 0xf0, 0x4f, 0xdf, 0x3, 0xf4, 0x9f, 0x38, 0xb7, 0x5b, 0x76, 0xd7, 0x7, 0x2f, 0xcb, 0xda, 0x3, 0xc8, 0x42, 0x94, 0x48, 0x6a, 0x15, 0x9f, 0x24, 0x0, 0xbe, 0x43, 0xe3, 0xfc, 0xc1, 0x49, 0x37, 0x4d, 0xdd, 0xf8, 0x8b, 0x2b, 0xff, 0xde, 0xd9, 0x75, 0xa8, 0x5, 0x76, 0x2b, 0x6, 0xe8, 0x37, 0x7e, 0xf6, 0x18, 0xeb, 0xb6, 0x9c, 0x6, 0x69, 0xf, 0x1a, 0xf3, 0xc1, 0x1f, 0xef, 0xbb, 0xe6, 0xdb, 0xb5, 0xa4, 0x37, 0xf2, 0x9a, 0xa5, 0xdc, 0xb2, 0x69, 0xe5, 0x69, 0xca, 0xec, 0xf8, 0xa5, 0xa4, 0x26, 0x48, 0xf2, 0x29, 0x8c, 0x82, 0x7b, 0x40, 0x3e, 0xae, 0xfc, 0x5f, 0x41, 0x82, 0x4d, 0x37, 0xf5, 0x18, 0xb7, 0xd7, 0xc1, 0xc3, 0x1f, 0xfe, 0xfd, 0x4f, 0x3e, 0xa3, 0x2a, 0x8b, 0xd0, 0xe9, 0x50, 0xf7, 0xc, 0x70, 0xf8, 0x39, 0xb3, 0x3e, 0x9, 0xb9, 0xa7, 0x48, 0xf6, 0x70, 0xb9, 0xd9, 0xd1, 0x90, 0x3b, 0xd2, 0x2b, 0x39, 0x9b, 0x4d, 0xaa, 0x69, 0xfa, 0x1f, 0xef, 0xbb, 0xfa, 0x8e, 0x5a, 0xd2, 0x3f, 0x6c, 0xdc, 0x8f, 0x9f, 0x91, 0xcd, 0x8c, 0x2, 0xe9, 0xe4, 0x43, 0x3, 0x7a, 0xc7, 0x80, 0x4, 0x80, 0xbb, 0x9c, 0x74, 0xd3, 0x77, 0xdd, 0xcc, 0xce, 0x1f, 0xbc, 0xfc, 0xd0, 0xd7, 0x6d, 0x67, 0xe3, 0xa9, 0x5a, 0xa8, 0x5b, 0x19, 0xa0, 0xdf, 0xf8, 0xd9, 0x47, 0x42, 0xee, 0x51, 0x72, 0x5b, 0x3e, 0x6b, 0xdd, 0xcc, 0x69, 0x0, 0xf6, 0xf4, 0x8, 0xe3, 0x75, 0xc3, 0x92, 0x1a, 0x6d, 0x66, 0xd7, 0x8f, 0xfa, 0x8d, 0x9f, 0xf5, 0xe7, 0x97, 0x7e, 0x71, 0xe5, 0xf3, 0x55, 0xe5, 0x31, 0xe1, 0xc6, 0x83, 0xdc, 0x5d, 0x5b, 0x46, 0x3, 0x7e, 0xb3, 0xce, 0x51, 0xbd, 0x74, 0xbb, 0xf0, 0x9b, 0xbb, 0x23, 0xd9, 0x23, 0x59, 0xf7, 0x4d, 0xa8, 0x34, 0xd4, 0x1d, 0x3, 0xf4, 0x3b, 0x67, 0xf6, 0x20, 0xc0, 0x1d, 0x65, 0xb3, 0xcd, 0x67, 0xc9, 0xcd, 0x9c, 0xa, 0x9f, 0x1a, 0xfe, 0xd8, 0x5b, 0xd0, 0x15, 0xb, 0xda, 0xcf, 0x66, 0x9a, 0x7f, 0xd8, 0x7f, 0xc2, 0x9c, 0xb1, 0x1b, 0x17, 0x4d, 0x7f, 0xa7, 0x92, 0x7c, 0xe, 0xff, 0xc2, 0xd, 0x69, 0x58, 0x77, 0x1c, 0x40, 0x17, 0x84, 0x3, 0x54, 0x48, 0x4a, 0xd2, 0x91, 0x75, 0x7, 0x38, 0x4d, 0x7b, 0xef, 0xd6, 0x2c, 0x50, 0x37, 0xc, 0xd0, 0x7f, 0xc2, 0x8d, 0x7, 0x3, 0x1a, 0x65, 0x33, 0xcd, 0x97, 0x58, 0x9b, 0x3d, 0x5, 0x0, 0x40, 0xe6, 0x5a, 0xa6, 0x82, 0x47, 0xbf, 0x13, 0x0, 0x21, 0x82, 0x90, 0xdc, 0xcf, 0xc8, 0x66, 0xbe, 0xd6, 0x6f, 0xfc, 0xac, 0xef, 0xbc, 0xf4, 0x8b, 0x2b, 0x77, 0xb5, 0x35, 0x3f, 0x93, 0x6a, 0xb4, 0x36, 0xb3, 0xe3, 0x7c, 0x1a, 0xe3, 0x8f, 0xe9, 0xe1, 0x21, 0x3f, 0x4, 0xe1, 0xd1, 0x40, 0x80, 0x24, 0xd1, 0x90, 0x10, 0x8c, 0x64, 0xf, 0x31, 0xa9, 0xa6, 0x2e, 0x6, 0x48, 0x2, 0x64, 0xb3, 0xdf, 0xb0, 0xd9, 0xe6, 0x2f, 0x1, 0x41, 0x6b, 0xf7, 0x9, 0x2f, 0x91, 0x1e, 0x23, 0x78, 0x40, 0x9f, 0x8, 0x34, 0x41, 0x90, 0x75, 0x33, 0xbb, 0xfe, 0xaf, 0xd3, 0xd0, 0x7d, 0xd9, 0x61, 0xe3, 0x7e, 0x78, 0xdf, 0x9f, 0x1e, 0xb8, 0x2e, 0x72, 0x3c, 0x1e, 0x30, 0xe9, 0xce, 0x94, 0xdb, 0xb2, 0x7d, 0x1f, 0xd2, 0xf4, 0x0, 0x6c, 0x1a, 0xb2, 0xbd, 0x65, 0xdd, 0xcf, 0x0, 0x30, 0x0, 0x41, 0x42, 0x91, 0xbd, 0x0, 0xb, 0xef, 0x49, 0x52, 0x9e, 0x7a, 0x40, 0x48, 0x7b, 0xee, 0x6, 0x62, 0x54, 0x49, 0xa8, 0x1b, 0x6, 0xb0, 0xd9, 0xcc, 0x39, 0x7e, 0x6b, 0xb, 0xda, 0x5c, 0xa8, 0xeb, 0x2f, 0x4, 0x7a, 0x81, 0x41, 0x3c, 0x3, 0x10, 0x36, 0xbb, 0xeb, 0xc7, 0x4e, 0x43, 0x8f, 0xd7, 0xfa, 0x4f, 0xb8, 0x71, 0x3, 0x68, 0xf6, 0x7, 0xb0, 0x37, 0x81, 0x34, 0x8, 0xca, 0xda, 0x46, 0xb7, 0x65, 0xeb, 0x27, 0x20, 0x3b, 0xd8, 0x75, 0xdd, 0xbe, 0x4, 0xf6, 0x83, 0x74, 0x30, 0x80, 0x40, 0xf0, 0x53, 0x25, 0x43, 0x0, 0xfd, 0xff, 0x22, 0xad, 0xcd, 0xee, 0xac, 0x59, 0x3, 0xe8, 0x7f, 0xee, 0x2d, 0xdd, 0x36, 0xde, 0x73, 0xe9, 0x8e, 0xce, 0xc0, 0x7b, 0xdd, 0x30, 0x0, 0x8d, 0xf3, 0xb2, 0xac, 0x3e, 0xe, 0xc0, 0x20, 0x4e, 0xc, 0xf, 0xc8, 0x1e, 0x3c, 0xf9, 0x8a, 0x3a, 0x49, 0x40, 0xf6, 0x60, 0xeb, 0xb6, 0xfc, 0x1f, 0xd2, 0xac, 0xb3, 0xb6, 0xf9, 0x24, 0x78, 0xad, 0xbb, 0x9b, 0x17, 0xb5, 0x90, 0x46, 0xca, 0x25, 0x46, 0x85, 0x52, 0x8e, 0xcb, 0xa7, 0x30, 0xdc, 0x4a, 0xa0, 0x21, 0xe8, 0x7d, 0xed, 0x36, 0x6f, 0xad, 0x89, 0x1, 0x8e, 0x38, 0x77, 0x6e, 0x5f, 0xeb, 0xb6, 0x9c, 0xde, 0x7f, 0xc2, 0x9c, 0xe7, 0x36, 0x2e, 0x9a, 0xbe, 0xb6, 0xc3, 0xf1, 0xde, 0xd1, 0x19, 0xc6, 0x41, 0xff, 0x9, 0xb3, 0xbf, 0x67, 0x33, 0xcd, 0x5f, 0x96, 0x6c, 0xcf, 0x70, 0xab, 0x8f, 0xa3, 0x43, 0x41, 0xb8, 0x4, 0x90, 0x16, 0x80, 0x41, 0xd8, 0x78, 0x43, 0x6, 0xef, 0x8a, 0xf4, 0x7a, 0x9, 0xf2, 0xfa, 0x7d, 0xff, 0xb1, 0x40, 0x0, 0x60, 0x51, 0xde, 0xc5, 0xf9, 0x11, 0x39, 0x23, 0x51, 0xd6, 0x69, 0xdc, 0xf3, 0x18, 0xc9, 0xbe, 0x61, 0x9c, 0x86, 0xad, 0x1b, 0xee, 0x9e, 0xba, 0xb5, 0xad, 0xf5, 0x3d, 0xe2, 0xbc, 0x5b, 0xbb, 0xc1, 0xba, 0x9f, 0xc8, 0x66, 0xb6, 0x2f, 0x86, 0x74, 0x28, 0x80, 0xb7, 0x4d, 0xba, 0xdb, 0xb9, 0xc6, 0x38, 0x4b, 0x37, 0xdc, 0x73, 0x59, 0x4b, 0x47, 0xe1, 0xbd, 0x6e, 0x18, 0xa0, 0xdf, 0x84, 0x39, 0xa7, 0x2b, 0xb3, 0x73, 0x2e, 0xa0, 0x3, 0x81, 0x60, 0x9c, 0x6f, 0x73, 0xb7, 0x1c, 0xc8, 0xb, 0x20, 0xf3, 0xad, 0x3a, 0xf4, 0x9c, 0x78, 0x3d, 0x95, 0x37, 0x13, 0x92, 0xc6, 0xf9, 0xb, 0x9d, 0xf4, 0x22, 0x63, 0x52, 0xb7, 0x3, 0x78, 0x3d, 0xdb, 0xbc, 0x6d, 0xe7, 0x9f, 0x1e, 0xf8, 0x6a, 0xac, 0x6d, 0xa0, 0xff, 0x84, 0x39, 0xfb, 0x4b, 0xee, 0x14, 0x9b, 0x69, 0xfe, 0x41, 0x90, 0x9a, 0x2f, 0x5b, 0xc0, 0xa4, 0x9b, 0xfe, 0xcd, 0x18, 0xe7, 0x96, 0xd, 0xf7, 0x4e, 0x7f, 0xb7, 0x23, 0xf0, 0x5e, 0x37, 0xc, 0xd0, 0xff, 0xdc, 0x9b, 0x3f, 0x65, 0x5b, 0xb6, 0x3f, 0x21, 0xd9, 0x4f, 0x41, 0x8, 0x24, 0xed, 0xb6, 0x95, 0x56, 0x65, 0xde, 0x25, 0x55, 0x53, 0x3f, 0xad, 0x9c, 0x26, 0xe0, 0x3f, 0x14, 0xf4, 0x1e, 0x26, 0x75, 0x3f, 0x9d, 0x86, 0x9f, 0xd2, 0x49, 0xaf, 0xec, 0xd1, 0xfb, 0x88, 0x6d, 0xc5, 0x56, 0xc2, 0xfe, 0x13, 0x6f, 0x3a, 0xcc, 0x66, 0x9b, 0xe7, 0x40, 0xee, 0x18, 0x0, 0x5e, 0xaf, 0x15, 0x24, 0x3, 0x8, 0x24, 0x8d, 0xd3, 0x70, 0x13, 0x4c, 0xea, 0xfb, 0x1b, 0xef, 0x9d, 0xf6, 0x6a, 0x7b, 0xe3, 0xdd, 0xb4, 0x77, 0x6, 0x6d, 0x85, 0x8d, 0xf7, 0x5c, 0xf6, 0xa, 0x69, 0x9a, 0x3d, 0x51, 0x3b, 0xc0, 0x26, 0xfd, 0xb, 0xa5, 0x9, 0x58, 0xaa, 0xa3, 0x28, 0xf7, 0x6d, 0x25, 0xe0, 0xa7, 0x95, 0x27, 0x3e, 0x94, 0xcf, 0xc0, 0xd3, 0x26, 0xad, 0x9b, 0x3d, 0xdb, 0x6d, 0xd9, 0xf1, 0x8c, 0x6d, 0xd9, 0xfe, 0xdc, 0x96, 0xd7, 0x57, 0x9f, 0x11, 0xfe, 0xfc, 0x88, 0x73, 0xe7, 0x9e, 0xec, 0xb6, 0xec, 0xd8, 0x28, 0xeb, 0x8e, 0xf1, 0xbe, 0xf5, 0xf1, 0x2f, 0x88, 0xa0, 0xcf, 0x48, 0xb4, 0xd6, 0x6d, 0x99, 0x66, 0xb3, 0x2d, 0xf3, 0xfb, 0x4f, 0x9c, 0x7b, 0x5c, 0x7b, 0xe3, 0xbd, 0x6e, 0x7a, 0x0, 0x0, 0xe8, 0x77, 0xce, 0xcf, 0x9e, 0xb3, 0x6e, 0xe6, 0x78, 0x20, 0x5a, 0xfa, 0xaf, 0x3f, 0x8, 0x86, 0x81, 0x7c, 0x61, 0xfd, 0x91, 0x41, 0x64, 0xa0, 0x5a, 0x38, 0x2b, 0x4c, 0xba, 0xf1, 0x3b, 0x4, 0x7a, 0x66, 0x5b, 0x76, 0x2c, 0xf4, 0xf8, 0xbb, 0xd5, 0xb0, 0x54, 0xd0, 0x4f, 0x5, 0x6a, 0x26, 0x8d, 0x79, 0x8d, 0x4e, 0xd3, 0xbf, 0xbd, 0xb4, 0x68, 0xda, 0x5d, 0xed, 0x55, 0x3, 0xa7, 0xf6, 0x24, 0x92, 0x83, 0x8f, 0xf, 0x1c, 0x9b, 0x2, 0x34, 0x9c, 0x40, 0x8f, 0x78, 0xcb, 0x4c, 0x3d, 0x41, 0xd0, 0x45, 0xf9, 0xe0, 0xcb, 0x2d, 0xbe, 0xd5, 0x92, 0x14, 0x24, 0xa2, 0xb7, 0x6c, 0x76, 0x82, 0xac, 0x7b, 0x16, 0x29, 0x27, 0x50, 0x21, 0x1, 0x85, 0xba, 0xba, 0x5c, 0x37, 0xe2, 0xdb, 0xbb, 0x72, 0x36, 0xb0, 0xbd, 0x61, 0xdd, 0x13, 0xf7, 0x3f, 0xf2, 0xec, 0x96, 0x77, 0xd6, 0x2f, 0xfe, 0x5d, 0x7b, 0xd4, 0xa0, 0x6e, 0x86, 0x0, 0xaf, 0x34, 0xce, 0x12, 0x0, 0xef, 0x7b, 0xf7, 0x86, 0xf9, 0x21, 0x60, 0x37, 0xb9, 0x8c, 0xf1, 0x94, 0xc3, 0x80, 0x71, 0x69, 0x40, 0xd2, 0x90, 0x6c, 0x0, 0xd4, 0x0, 0x18, 0x79, 0x17, 0x8, 0x18, 0x45, 0xa4, 0xe1, 0x31, 0x81, 0x31, 0xfe, 0x7b, 0x3, 0x40, 0xfb, 0x65, 0x33, 0xbb, 0xbe, 0xde, 0x6f, 0xc2, 0x9c, 0xaf, 0xb4, 0x7, 0xca, 0xeb, 0xa6, 0x7, 0xe8, 0x3f, 0x71, 0xee, 0x4, 0x4f, 0x38, 0x52, 0x3f, 0xf8, 0xb6, 0x9e, 0xd0, 0xe8, 0xba, 0xfb, 0x5c, 0x44, 0xae, 0xfd, 0xe7, 0xee, 0x1, 0xbf, 0x67, 0x80, 0x7f, 0x11, 0xa1, 0x77, 0x45, 0xdf, 0x93, 0xa1, 0x74, 0x82, 0x1e, 0x61, 0x4f, 0xca, 0xe, 0xea, 0x35, 0xe8, 0x2c, 0xe7, 0x9d, 0xf5, 0x8b, 0x97, 0x26, 0x89, 0xf7, 0xba, 0x30, 0x4, 0xf5, 0x9f, 0x78, 0xd3, 0x2c, 0x9b, 0xdd, 0x75, 0x36, 0xa0, 0x3e, 0xde, 0x18, 0x69, 0x24, 0x28, 0x6c, 0x0, 0xde, 0x1d, 0x81, 0x31, 0xf7, 0x6d, 0x6, 0x6f, 0x14, 0xf4, 0x70, 0x21, 0xb0, 0x1b, 0xc8, 0xee, 0x49, 0x17, 0xb2, 0xd3, 0x19, 0xe0, 0xf0, 0x73, 0x66, 0x3d, 0xad, 0x6c, 0xcb, 0x48, 0x0, 0xdd, 0xbd, 0x39, 0x1e, 0xdf, 0x74, 0x53, 0xef, 0xc3, 0x7f, 0x87, 0x40, 0xce, 0xe4, 0xd, 0x1a, 0x67, 0xb5, 0x71, 0xd2, 0xd7, 0x27, 0x9d, 0x43, 0x62, 0xc, 0x70, 0xc4, 0xb9, 0x73, 0x7f, 0x24, 0x61, 0x1b, 0x8d, 0xf3, 0xd3, 0xd, 0x77, 0x4f, 0x7d, 0xbf, 0x5c, 0xfc, 0x81, 0x93, 0xee, 0xd8, 0x23, 0xdb, 0xb2, 0xe3, 0x31, 0xd9, 0xec, 0x9, 0x5e, 0x3f, 0x97, 0x13, 0x80, 0xbb, 0x88, 0xef, 0x83, 0x6f, 0xc0, 0x24, 0xe9, 0xbc, 0x63, 0x9c, 0xf4, 0xe2, 0x75, 0x3f, 0xbf, 0x38, 0x71, 0xb, 0x61, 0x22, 0x98, 0x3e, 0xe2, 0xbc, 0x5b, 0x47, 0xd8, 0xcc, 0xce, 0x7, 0x25, 0x75, 0xa3, 0x71, 0xd6, 0x80, 0xe6, 0x75, 0xd2, 0xfc, 0x99, 0xc6, 0x99, 0xb7, 0xe1, 0xee, 0xa9, 0x7f, 0x2e, 0x8e, 0xdf, 0xff, 0xdc, 0x5b, 0xe, 0x95, 0xdb, 0x72, 0x87, 0x6c, 0x76, 0x14, 0xe9, 0xab, 0xc2, 0x5e, 0x4d, 0xe1, 0x49, 0xc7, 0x51, 0xc5, 0x8c, 0x33, 0xb9, 0x87, 0xe7, 0x85, 0x54, 0xe2, 0x9b, 0x62, 0x3, 0x6f, 0xb9, 0xb4, 0x63, 0xb5, 0xb4, 0x88, 0xf0, 0xa8, 0xef, 0x2a, 0x29, 0x6f, 0xeb, 0x6f, 0x72, 0xbe, 0x67, 0x0, 0x41, 0xe7, 0x91, 0x74, 0xd3, 0x9e, 0xe3, 0xd7, 0x2e, 0x98, 0xb2, 0xb3, 0x1a, 0xfa, 0x94, 0x82, 0x9a, 0x19, 0xe0, 0x88, 0xf3, 0xe7, 0xf5, 0x50, 0x76, 0xc7, 0xc3, 0xb2, 0xee, 0x71, 0x80, 0x42, 0x5a, 0x85, 0xd9, 0x42, 0x63, 0xd6, 0x80, 0xce, 0x4a, 0x1a, 0xe7, 0x77, 0x26, 0x95, 0x7e, 0x74, 0xdd, 0x82, 0x29, 0x1f, 0x1c, 0x71, 0xfe, 0x6d, 0x43, 0x6c, 0x66, 0xd7, 0x4d, 0x90, 0x7b, 0x54, 0xa8, 0x18, 0xc2, 0x6e, 0x3e, 0xe0, 0x27, 0xa, 0xca, 0xd9, 0x1c, 0x9, 0x3a, 0xaf, 0x9a, 0x54, 0xc3, 0xbf, 0x6f, 0xb8, 0xfb, 0xd2, 0x85, 0xed, 0x91, 0x55, 0xcd, 0x43, 0x80, 0xdc, 0xe6, 0x1f, 0xcb, 0xba, 0xbe, 0xf1, 0xc6, 0x84, 0xed, 0xf0, 0x7b, 0xc9, 0xba, 0xa3, 0x0, 0x77, 0x94, 0xac, 0x73, 0x86, 0xac, 0x3b, 0xae, 0xff, 0xc4, 0xb9, 0x7f, 0x57, 0xb6, 0x79, 0x38, 0x64, 0x8f, 0xf2, 0x6c, 0xf4, 0xb9, 0x7e, 0xdf, 0x17, 0xf8, 0xca, 0xb5, 0xe0, 0xb6, 0x98, 0xf5, 0x2b, 0x69, 0x95, 0xc5, 0x3e, 0x9f, 0xe1, 0xfb, 0x72, 0x71, 0xdb, 0x52, 0xd6, 0xf0, 0x77, 0x71, 0xe5, 0x2c, 0xec, 0x11, 0x3c, 0xe9, 0x97, 0xb9, 0xf9, 0x26, 0x1a, 0xe7, 0xa9, 0xc6, 0x6e, 0x3d, 0xef, 0xa9, 0x95, 0x4e, 0x71, 0x50, 0x53, 0xab, 0x1b, 0x70, 0xfe, 0x6d, 0x5f, 0xb0, 0x99, 0x9d, 0xbf, 0x90, 0x37, 0x8b, 0x9f, 0xa3, 0x62, 0x48, 0x82, 0x97, 0x82, 0x3c, 0xa, 0x3c, 0xee, 0x68, 0x25, 0x85, 0xad, 0xfd, 0x5d, 0xad, 0x3f, 0x4, 0x9e, 0x25, 0x90, 0x34, 0x4e, 0xea, 0x45, 0x3a, 0xd, 0xd7, 0xad, 0xbf, 0xeb, 0xe2, 0xe7, 0xda, 0x2b, 0xaf, 0xaa, 0x7b, 0x80, 0x81, 0x93, 0xee, 0x38, 0xd8, 0xcd, 0xec, 0xfc, 0xbe, 0x2, 0xa3, 0x95, 0xef, 0x58, 0xc5, 0x90, 0xff, 0x4e, 0xce, 0x98, 0x27, 0x8, 0x26, 0x4f, 0x64, 0x8f, 0xf8, 0x7e, 0xb7, 0x5f, 0xcd, 0x5c, 0x5d, 0x12, 0xf3, 0x7b, 0xd5, 0xb0, 0x5e, 0xa9, 0x6f, 0x8a, 0x1c, 0xc7, 0x2b, 0xae, 0x47, 0xe1, 0x77, 0xde, 0x9d, 0x71, 0xee, 0x69, 0x4f, 0xe2, 0x3, 0x55, 0x5a, 0x2, 0x7, 0x4c, 0x9a, 0xd7, 0x60, 0x6d, 0xf6, 0x5a, 0x59, 0xfb, 0x69, 0x7f, 0xf2, 0xc6, 0x9b, 0x5d, 0x2f, 0x12, 0xdf, 0x73, 0xcf, 0x45, 0xe3, 0xbb, 0x37, 0xe7, 0x91, 0x73, 0xac, 0xa9, 0x1c, 0x2a, 0xfc, 0x46, 0x2a, 0xe8, 0x9f, 0x15, 0xfc, 0x13, 0x50, 0xe0, 0x3e, 0x20, 0x95, 0x49, 0xa8, 0xd4, 0xc4, 0x12, 0x8b, 0x7e, 0x2b, 0xad, 0x7, 0x8b, 0x5f, 0x30, 0x5e, 0x8e, 0x4c, 0x10, 0x2a, 0x66, 0x80, 0x81, 0x17, 0xce, 0x77, 0x20, 0x3b, 0xda, 0x66, 0x5b, 0xae, 0xcd, 0x9b, 0xc1, 0xc3, 0x53, 0x76, 0x9d, 0x7b, 0x29, 0x34, 0x43, 0xe7, 0xdf, 0xfb, 0x73, 0xed, 0x1e, 0x81, 0x25, 0xd0, 0x37, 0x34, 0xe4, 0xae, 0xe0, 0x1d, 0xa3, 0x6d, 0x73, 0xf2, 0xd3, 0xca, 0xdd, 0xb7, 0xff, 0xe5, 0x33, 0xa9, 0xdc, 0x2f, 0xc, 0x98, 0x74, 0xc7, 0xf1, 0xed, 0xc9, 0x0, 0x15, 0xb7, 0xbf, 0x41, 0x17, 0xce, 0xef, 0x93, 0x6d, 0xd9, 0xf9, 0x0, 0xac, 0x3b, 0xd2, 0xeb, 0xc0, 0xe3, 0x54, 0xb7, 0x4a, 0x8a, 0x50, 0x4a, 0x65, 0x2, 0x4a, 0xb, 0x5c, 0x39, 0xc8, 0x77, 0xa2, 0xa, 0xd4, 0xa7, 0x82, 0xb4, 0x3e, 0x0, 0xf9, 0x3e, 0x80, 0x6d, 0x0, 0x76, 0x92, 0xdc, 0x5, 0xd2, 0x42, 0x8, 0xe4, 0x91, 0x14, 0x80, 0x26, 0x9, 0xdd, 0x20, 0xbb, 0xf, 0xa0, 0x5e, 0x90, 0x6f, 0x2a, 0x67, 0x4c, 0x1e, 0x35, 0x6b, 0x2e, 0x11, 0xf5, 0x93, 0xe7, 0x13, 0x0, 0xc2, 0x42, 0x32, 0x34, 0xce, 0x93, 0x26, 0xd5, 0x78, 0xd9, 0xba, 0x85, 0x17, 0xbd, 0x5a, 0x5b, 0x5e, 0xa5, 0x4b, 0xd0, 0x26, 0x18, 0x7c, 0xc5, 0x33, 0xcc, 0x7e, 0xf8, 0xea, 0x19, 0x36, 0xb3, 0xf3, 0x21, 0x0, 0x16, 0x81, 0x12, 0x1f, 0xae, 0x8a, 0xe2, 0x13, 0x2d, 0x18, 0xf2, 0x22, 0x4, 0xe7, 0x3c, 0xef, 0x7, 0xe6, 0xaf, 0xfc, 0x87, 0xad, 0xc8, 0xee, 0x8b, 0x1d, 0xa, 0xd6, 0x8a, 0x84, 0xe6, 0x66, 0xfd, 0x98, 0x2d, 0x34, 0xce, 0x3f, 0x24, 0xbc, 0x4d, 0x9a, 0x4d, 0x30, 0x66, 0x1d, 0xc0, 0x97, 0x48, 0xfe, 0x15, 0xe0, 0x3b, 0x20, 0xdf, 0x23, 0xd4, 0xec, 0x5a, 0x65, 0xd2, 0xe9, 0x86, 0x46, 0xeb, 0x66, 0xba, 0x1, 0xd8, 0xd7, 0x5a, 0xdb, 0x7, 0xd2, 0x0, 0xc9, 0x8e, 0x24, 0x74, 0xb0, 0xa4, 0x8f, 0x1, 0x3a, 0x0, 0xb2, 0xfb, 0x86, 0xab, 0xc2, 0x90, 0xcf, 0x7a, 0x1, 0x33, 0xa8, 0x75, 0x7d, 0xa, 0xe4, 0xfd, 0x8, 0x33, 0x40, 0x41, 0x13, 0xa, 0x3b, 0xc6, 0x7a, 0x35, 0xb2, 0x74, 0x1a, 0xe6, 0x34, 0xef, 0xdc, 0x7e, 0xd5, 0x5f, 0x1e, 0xb8, 0x3a, 0xf1, 0x15, 0x48, 0x15, 0x9, 0x81, 0xee, 0x96, 0xbf, 0xd, 0x92, 0xdb, 0xf2, 0x7d, 0x80, 0xa0, 0x6f, 0xb0, 0xf7, 0xb0, 0x9f, 0x9b, 0xb7, 0x40, 0xde, 0x23, 0xab, 0x88, 0x82, 0xb9, 0xa7, 0xd0, 0xff, 0x1c, 0xc5, 0x43, 0x2c, 0xc3, 0xa8, 0xc6, 0xdb, 0x2a, 0x2c, 0xb0, 0x93, 0x4, 0x62, 0x86, 0xfc, 0x29, 0x78, 0x80, 0x7c, 0xf, 0x30, 0x9b, 0x4, 0xb3, 0x96, 0x4e, 0x6a, 0x71, 0x3a, 0xd5, 0xf0, 0xe0, 0xea, 0x3b, 0x27, 0x95, 0x33, 0xa0, 0xb4, 0x0, 0xd8, 0xa, 0xe0, 0x2d, 0x0, 0x1b, 0x1, 0x3c, 0x15, 0xbc, 0x18, 0x30, 0xe9, 0xce, 0x7e, 0xb2, 0x99, 0x89, 0x80, 0x8e, 0x83, 0xec, 0xa1, 0x92, 0x3d, 0x8, 0x42, 0x63, 0xa0, 0xc1, 0x7a, 0x79, 0x2a, 0x50, 0x79, 0x42, 0x66, 0xcc, 0xbc, 0xab, 0x40, 0x58, 0xd, 0xca, 0xd7, 0x3b, 0xa8, 0x17, 0xfc, 0x70, 0xbf, 0x4a, 0xfe, 0xa, 0x88, 0xc0, 0x30, 0x26, 0xc1, 0x48, 0xee, 0x99, 0x4d, 0xdd, 0xf7, 0x5a, 0xe, 0xe0, 0xf6, 0xa4, 0x19, 0xa0, 0xa2, 0x1e, 0x60, 0xe0, 0x5, 0x77, 0x9e, 0xea, 0x66, 0x76, 0x3e, 0x26, 0xc9, 0x84, 0xb1, 0xdf, 0x91, 0x10, 0xb2, 0x15, 0xcb, 0xd7, 0x32, 0x7c, 0xe, 0xe0, 0xfb, 0x24, 0xdf, 0xa2, 0x49, 0xdd, 0x6c, 0x1c, 0x67, 0xf6, 0xda, 0x5, 0x53, 0xda, 0xbc, 0x48, 0xa4, 0xad, 0x30, 0x60, 0xd2, 0xbc, 0x43, 0x21, 0xf7, 0xbb, 0x92, 0x1d, 0x29, 0xeb, 0x7e, 0x2, 0x52, 0x93, 0xbf, 0x4c, 0x2d, 0xd4, 0x10, 0x12, 0xad, 0x6b, 0xde, 0xa7, 0x91, 0xce, 0x12, 0xa6, 0xf6, 0x38, 0x75, 0xfd, 0xc2, 0xb, 0x9a, 0x93, 0xcc, 0xa3, 0x32, 0x6, 0x98, 0x7c, 0x57, 0xa3, 0xcd, 0x36, 0x9f, 0x23, 0xb7, 0xe5, 0x4e, 0x40, 0x4e, 0xe0, 0x80, 0xd9, 0x51, 0xf6, 0xfb, 0x80, 0xf8, 0x61, 0x84, 0x4b, 0xd8, 0x49, 0x60, 0x87, 0x49, 0x37, 0x7e, 0x7b, 0xdd, 0xc2, 0x8b, 0x6e, 0x6c, 0xf7, 0x42, 0x0, 0x18, 0x30, 0xf9, 0xee, 0x94, 0x32, 0xdb, 0xe7, 0xca, 0xba, 0xa7, 0x1, 0xda, 0x3f, 0xb7, 0x78, 0x25, 0xef, 0x26, 0x98, 0x38, 0x42, 0x24, 0xb9, 0x34, 0xa9, 0x7b, 0x36, 0xdc, 0x3d, 0xf5, 0x82, 0x24, 0xd3, 0xad, 0x5c, 0x8, 0x9c, 0xbc, 0x70, 0xf, 0xeb, 0x66, 0x2f, 0xb3, 0xd9, 0xe6, 0x19, 0xf0, 0xc7, 0x5b, 0x4f, 0x14, 0xc8, 0x69, 0x57, 0x25, 0xd2, 0x2c, 0x67, 0x15, 0xb, 0xc7, 0x29, 0xf2, 0xe5, 0xf7, 0xa7, 0x46, 0xe1, 0x59, 0x19, 0xe1, 0x79, 0xd2, 0x3a, 0x5b, 0x61, 0x9c, 0xdb, 0x68, 0x52, 0x5f, 0x5f, 0xbf, 0x70, 0x72, 0x87, 0xb9, 0x52, 0x7, 0x30, 0x70, 0xf2, 0xcf, 0xbb, 0xb9, 0x2d, 0xdb, 0x97, 0x41, 0xb6, 0x2f, 0xc0, 0x3d, 0x2, 0xe7, 0x64, 0x32, 0xf0, 0x48, 0xe, 0x44, 0xa4, 0x52, 0xe, 0xe6, 0xc5, 0x78, 0x88, 0x14, 0x72, 0x25, 0x89, 0xa0, 0x59, 0xed, 0xa4, 0x1a, 0x3f, 0xb7, 0x6e, 0xe1, 0x94, 0xb7, 0xc2, 0x2f, 0x47, 0x5e, 0xb3, 0x94, 0x3b, 0xdf, 0x7b, 0xc5, 0xd0, 0x69, 0xa0, 0xcd, 0x6c, 0x27, 0x4d, 0x9a, 0x0, 0xdd, 0x75, 0xb, 0x2e, 0x70, 0xcb, 0xd5, 0xa1, 0x2a, 0x2e, 0x1d, 0x34, 0xf9, 0xe7, 0x7b, 0x58, 0xb7, 0xe5, 0xdb, 0x36, 0xdb, 0xf2, 0x75, 0x2, 0x26, 0x34, 0xf0, 0x33, 0x69, 0x57, 0xae, 0x70, 0x97, 0xf, 0x8f, 0xf8, 0x2, 0x0, 0x63, 0xcc, 0x66, 0xa6, 0x1a, 0xc7, 0xae, 0x5b, 0x30, 0x39, 0x51, 0x7, 0x89, 0x6a, 0x60, 0xc0, 0xa4, 0x79, 0xd7, 0xd9, 0x6c, 0xf3, 0x37, 0x0, 0xed, 0xcb, 0xb0, 0x47, 0x50, 0x72, 0x26, 0xab, 0x80, 0xe9, 0x41, 0x93, 0x7a, 0x7e, 0xc3, 0xdd, 0x53, 0x8f, 0x1f, 0x34, 0xe5, 0xee, 0x46, 0x9b, 0x6d, 0xa6, 0x71, 0x1a, 0x9a, 0x24, 0xb7, 0x2f, 0xa4, 0x8f, 0x9, 0xea, 0x49, 0xa0, 0x87, 0xa4, 0x3, 0x69, 0x9c, 0x17, 0x20, 0xf7, 0xf9, 0x75, 0xb, 0x2f, 0xca, 0x94, 0x4a, 0xbc, 0xea, 0xc2, 0xd, 0xbc, 0x70, 0x61, 0x83, 0x6c, 0xf6, 0xdf, 0xe5, 0xb6, 0x7c, 0x13, 0x64, 0xb0, 0x20, 0x23, 0xbf, 0xd8, 0x22, 0x49, 0x8, 0x98, 0x4a, 0x82, 0xa4, 0xac, 0x71, 0x52, 0xcf, 0x3b, 0xd, 0xdd, 0xce, 0x5e, 0x33, 0xef, 0xbc, 0xf, 0x92, 0xcf, 0xac, 0x5a, 0x7c, 0x2c, 0x18, 0x63, 0xb3, 0xbb, 0xe6, 0xc9, 0xda, 0x83, 0x3d, 0x43, 0x17, 0x3, 0x49, 0xbe, 0xf6, 0x6, 0x91, 0x5f, 0xdc, 0x2, 0x1, 0xbb, 0x48, 0xf3, 0x1a, 0xa4, 0xde, 0x80, 0x7a, 0x2, 0x28, 0xd2, 0x81, 0x82, 0x7b, 0xae, 0x34, 0xa9, 0xc6, 0xeb, 0xd6, 0x2d, 0x9c, 0xfc, 0x54, 0xa9, 0xa4, 0xab, 0xf6, 0x9, 0x5c, 0xb7, 0xe0, 0x82, 0x16, 0xa7, 0xb1, 0xfb, 0x7f, 0x99, 0x54, 0xe3, 0x75, 0x4, 0xb2, 0x8, 0x69, 0x33, 0x89, 0x9b, 0x45, 0x7c, 0x24, 0x90, 0xcc, 0x1a, 0x27, 0xfd, 0xf0, 0xfa, 0xbb, 0x2e, 0x19, 0x53, 0x4f, 0xc4, 0xf7, 0xf0, 0x71, 0xe1, 0xd3, 0x26, 0xd5, 0x78, 0xa, 0xe9, 0x6c, 0x40, 0x41, 0xd9, 0x6b, 0x77, 0x6d, 0x43, 0xe1, 0x6f, 0x23, 0x64, 0xf, 0x3, 0xb4, 0x17, 0x8a, 0xde, 0x15, 0xde, 0x6b, 0x88, 0xa0, 0x7f, 0x29, 0x57, 0xee, 0x9a, 0x9c, 0x42, 0xd7, 0xdc, 0x3e, 0x3e, 0xeb, 0x34, 0x74, 0x9f, 0x69, 0x52, 0x4d, 0x53, 0x4, 0xbe, 0xdf, 0x76, 0x8b, 0x60, 0x18, 0x4a, 0x85, 0x31, 0xc7, 0xd6, 0x5e, 0xbf, 0x9f, 0x7a, 0x72, 0xfd, 0x5d, 0x17, 0x9f, 0x95, 0x18, 0xd5, 0x12, 0x86, 0x75, 0xb, 0x26, 0xff, 0xc9, 0x34, 0x34, 0x9d, 0x49, 0xe3, 0x2c, 0xf7, 0x2d, 0x8e, 0xfe, 0xf0, 0x15, 0x26, 0x63, 0x31, 0x59, 0x4b, 0x85, 0x79, 0xcf, 0x7e, 0x7, 0x2b, 0xd2, 0xc8, 0xf3, 0x31, 0xd, 0x2e, 0x8a, 0x34, 0xc1, 0xd4, 0x51, 0x51, 0x47, 0x6c, 0xc, 0x81, 0x4f, 0x7e, 0xea, 0xc2, 0x87, 0x4a, 0xf6, 0x3e, 0x35, 0x7b, 0x5, 0xaf, 0xbe, 0xfd, 0x9c, 0x2c, 0x53, 0xd, 0xf7, 0x93, 0xdc, 0x51, 0x64, 0xa, 0x29, 0x1, 0x51, 0xcc, 0x50, 0x18, 0x26, 0xbf, 0x1f, 0x83, 0xe7, 0x2b, 0x0, 0xe3, 0xa4, 0x9f, 0x6c, 0x49, 0xf7, 0x3a, 0xbd, 0xdd, 0xa8, 0x97, 0x10, 0xac, 0x9b, 0x7f, 0xc1, 0x2b, 0x26, 0xd5, 0x38, 0x85, 0xc6, 0x59, 0x82, 0xfc, 0xec, 0x28, 0xa2, 0x99, 0x1c, 0x65, 0xc2, 0xf2, 0xcf, 0xcc, 0xdb, 0xa9, 0xc3, 0xef, 0x99, 0x7f, 0x95, 0x8b, 0x27, 0x4f, 0x39, 0x12, 0xac, 0xcd, 0x9e, 0xd7, 0x9d, 0x1f, 0x4e, 0x2c, 0x55, 0xde, 0x44, 0xdc, 0xc2, 0x6d, 0x36, 0xd3, 0xf, 0x40, 0xda, 0xa3, 0x17, 0x13, 0x71, 0xe7, 0x26, 0xd, 0x5, 0x58, 0x6f, 0x4a, 0x3c, 0xfd, 0xa8, 0x93, 0x6e, 0x1a, 0xff, 0xf2, 0xbc, 0xd3, 0x77, 0x8b, 0xcd, 0x98, 0xd6, 0xce, 0xbf, 0x60, 0x23, 0x4c, 0x6a, 0x16, 0xe9, 0xbc, 0xec, 0x4d, 0x31, 0x18, 0xc9, 0x53, 0x97, 0x6a, 0xc6, 0x4b, 0x1b, 0x2e, 0x6, 0xb3, 0x6d, 0x90, 0xfa, 0xca, 0xaa, 0x7f, 0xfb, 0x33, 0x80, 0x9b, 0x99, 0x1, 0x70, 0xdf, 0x9a, 0x13, 0x2a, 0x4, 0x1, 0x34, 0x34, 0xce, 0x5a, 0x9a, 0xf4, 0xd, 0x6b, 0xe6, 0x9d, 0xf7, 0x61, 0x7b, 0x11, 0xac, 0x3d, 0x60, 0xfd, 0xc2, 0xc9, 0xf7, 0xc0, 0xa4, 0x6e, 0x7, 0xb9, 0xcd, 0xb7, 0x57, 0x75, 0x98, 0xd1, 0x2c, 0xbc, 0x48, 0x56, 0x50, 0x8f, 0x52, 0x71, 0x6b, 0x66, 0x80, 0x23, 0xa7, 0xdc, 0xb5, 0x17, 0x60, 0x87, 0x92, 0x48, 0xe5, 0xf5, 0xde, 0x44, 0xc4, 0x1e, 0x92, 0xdc, 0x42, 0x93, 0x5a, 0xb0, 0x76, 0xfe, 0xa4, 0xc7, 0x3a, 0xa, 0x79, 0x49, 0x82, 0x31, 0xce, 0x2c, 0x9a, 0xd4, 0xbd, 0xbe, 0x8d, 0x3a, 0x98, 0x25, 0x69, 0xf7, 0x19, 0xc5, 0x80, 0xf8, 0xbe, 0xf6, 0x7c, 0xe4, 0xc0, 0xc9, 0xb, 0x87, 0xc4, 0x96, 0xb1, 0x96, 0xa, 0x1e, 0x7d, 0xed, 0xef, 0x8, 0x72, 0x38, 0x80, 0xb4, 0x97, 0xa1, 0xd7, 0xcd, 0xd5, 0xd2, 0x83, 0xf9, 0x5, 0xf7, 0x2a, 0xe0, 0xa4, 0x1e, 0x0, 0x4d, 0xe2, 0xf6, 0xef, 0x8e, 0x82, 0xb5, 0xf3, 0x27, 0x6d, 0x31, 0x4e, 0xfa, 0x36, 0x18, 0x67, 0x29, 0xfc, 0x3e, 0xd9, 0x9f, 0xe9, 0xab, 0xc, 0x27, 0x11, 0xf7, 0x88, 0x9, 0xf7, 0x47, 0x1, 0xff, 0x3f, 0x1, 0x6b, 0x4f, 0x80, 0x74, 0x76, 0x5c, 0x19, 0x6b, 0x62, 0x80, 0x6d, 0xff, 0xd8, 0x48, 0xeb, 0x66, 0xcf, 0x25, 0x4d, 0xca, 0x2f, 0x51, 0xcd, 0xeb, 0x79, 0x7c, 0x75, 0x9f, 0xa0, 0x79, 0x15, 0x30, 0xf7, 0xaf, 0x9b, 0x3f, 0xa9, 0xa2, 0xdd, 0xbf, 0xea, 0xd, 0xd6, 0xde, 0x79, 0xfe, 0xb, 0x34, 0xa9, 0xc5, 0x0, 0x0, 0x1a, 0x11, 0xac, 0x58, 0x31, 0x64, 0xc4, 0x3d, 0x63, 0xc2, 0x8b, 0xf0, 0x28, 0x92, 0xd, 0x24, 0xfa, 0xc6, 0x95, 0xaf, 0x26, 0x6, 0x58, 0x7f, 0xd7, 0xc5, 0x56, 0x6e, 0x76, 0x2c, 0xa0, 0x34, 0x69, 0xc2, 0x5b, 0xaf, 0x54, 0x7e, 0x91, 0x81, 0xe4, 0xef, 0xf5, 0x22, 0xc6, 0x59, 0x44, 0x63, 0x1e, 0xef, 0x6c, 0x2, 0x26, 0x1, 0xa4, 0x59, 0x4c, 0x93, 0x7a, 0x12, 0xfe, 0x7e, 0x46, 0x28, 0x35, 0xc, 0xb0, 0xb6, 0x6, 0x54, 0x98, 0x16, 0x82, 0x6e, 0xb5, 0xf1, 0xd3, 0xe3, 0xe7, 0x46, 0xd2, 0xba, 0x26, 0x6, 0x18, 0x74, 0xd1, 0x3d, 0x7d, 0x41, 0xf4, 0xf6, 0xc4, 0x76, 0x28, 0xa7, 0xf2, 0x56, 0x39, 0xec, 0xfb, 0x92, 0x3f, 0x49, 0xf3, 0x16, 0x8d, 0xf3, 0xd4, 0xda, 0x3b, 0xcf, 0xdf, 0xde, 0xd9, 0xc4, 0x4b, 0x2, 0xd6, 0xde, 0x79, 0xfe, 0x5a, 0x3a, 0xa9, 0xf9, 0x5e, 0x25, 0x73, 0xe, 0x1f, 0x25, 0xc4, 0x9f, 0xda, 0x2f, 0xcf, 0xaf, 0xc4, 0x50, 0x9e, 0xf5, 0xb4, 0x67, 0x63, 0xb7, 0x1e, 0x7, 0x46, 0x95, 0xad, 0x6a, 0x6, 0x18, 0x7c, 0xc9, 0xfd, 0x29, 0x59, 0x3b, 0xa, 0xa0, 0x4d, 0xa2, 0xe4, 0x81, 0x1, 0x3, 0x20, 0x68, 0x9c, 0xf9, 0x34, 0xce, 0x8a, 0xce, 0x26, 0x5c, 0xa2, 0x40, 0xf3, 0x57, 0xd2, 0xbc, 0x8b, 0x76, 0x76, 0x9f, 0xb, 0x5c, 0xe2, 0xfc, 0x1d, 0x67, 0xe0, 0x73, 0xc3, 0xc1, 0x10, 0x3e, 0x1d, 0x55, 0xac, 0xaa, 0x19, 0x60, 0xf5, 0x6d, 0x67, 0x67, 0x9, 0x7d, 0x26, 0x34, 0xd3, 0x55, 0x93, 0x9a, 0xc3, 0xdc, 0x46, 0x4e, 0x0, 0x4d, 0x6a, 0xd9, 0x9a, 0x3b, 0x26, 0xee, 0xd6, 0x63, 0x7f, 0x31, 0x18, 0x63, 0xd6, 0x9b, 0x54, 0xc3, 0x6c, 0xff, 0x51, 0x91, 0x5b, 0x91, 0xd7, 0x0, 0x85, 0x3e, 0x9, 0x9e, 0x22, 0xe8, 0xd, 0xa5, 0xe6, 0x15, 0x3a, 0xa9, 0xfb, 0x49, 0xf3, 0x46, 0xd4, 0x77, 0x6d, 0xf6, 0x8, 0x1a, 0x74, 0xd1, 0xdd, 0x4d, 0xa4, 0xb3, 0xbf, 0xac, 0xed, 0x2b, 0xe8, 0x40, 0x2, 0x7b, 0x43, 0xee, 0x98, 0xdc, 0x86, 0x8d, 0xc9, 0xa8, 0xb9, 0x24, 0xb9, 0x9, 0xe0, 0xa6, 0x24, 0x91, 0x53, 0xf, 0xb0, 0xfa, 0xf6, 0x9, 0x9b, 0x7, 0x7f, 0x71, 0xd1, 0x43, 0x0, 0xfe, 0x3, 0x0, 0x61, 0x12, 0x30, 0xc1, 0x84, 0x26, 0x80, 0x48, 0x12, 0xb2, 0xca, 0xeb, 0x1, 0x7c, 0x97, 0xc6, 0x79, 0x96, 0x74, 0x16, 0x64, 0x65, 0x16, 0x6f, 0xb8, 0x73, 0x42, 0x36, 0x2a, 0x89, 0x58, 0x6, 0x18, 0x74, 0xd1, 0xbd, 0x3d, 0x21, 0x1d, 0xe, 0xe8, 0x40, 0x90, 0x7b, 0x41, 0xea, 0x25, 0x65, 0x8f, 0x90, 0x75, 0x8f, 0x83, 0xf4, 0xe9, 0x60, 0xe1, 0x7a, 0x68, 0x51, 0x67, 0xad, 0x1e, 0x31, 0x9e, 0x3b, 0x21, 0x53, 0x33, 0x4, 0xac, 0x69, 0x2f, 0x42, 0x74, 0x26, 0x8, 0xb0, 0xa0, 0x79, 0x1f, 0xd2, 0x3e, 0x9, 0x35, 0x97, 0xc0, 0x21, 0xc7, 0x73, 0x8e, 0x1, 0x29, 0x70, 0x33, 0xc9, 0x55, 0x34, 0xa9, 0x87, 0x8c, 0x93, 0x9a, 0xbf, 0xfa, 0xf6, 0xf1, 0x9b, 0x4b, 0x25, 0x11, 0xc9, 0x0, 0x83, 0xbf, 0xb8, 0x68, 0x7f, 0x2b, 0xf7, 0x7c, 0x59, 0x77, 0x3a, 0x64, 0xf, 0x2f, 0xcc, 0x33, 0xa7, 0xb0, 0x7b, 0x4e, 0xa, 0xf0, 0xf9, 0xad, 0xb6, 0x1e, 0x20, 0x67, 0x2c, 0x37, 0x8e, 0xf3, 0xfc, 0x9a, 0x3b, 0x26, 0x6e, 0x4b, 0x8, 0xe7, 0x75, 0x5, 0x92, 0xb6, 0x3, 0xf8, 0x33, 0x80, 0x91, 0x40, 0xa2, 0xdb, 0xd7, 0x51, 0xe0, 0x3f, 0xe8, 0x38, 0xaf, 0x11, 0xe6, 0x41, 0x90, 0x3f, 0x5d, 0x3b, 0xef, 0xdc, 0x36, 0x9, 0xd0, 0x31, 0x3d, 0x80, 0xc6, 0xcb, 0x75, 0xbf, 0x5, 0xe8, 0x63, 0x60, 0xd8, 0xf1, 0xb3, 0xc0, 0x41, 0x23, 0x34, 0x3f, 0xd1, 0x26, 0xb7, 0xed, 0x92, 0x15, 0xc8, 0xff, 0x7c, 0x74, 0xd7, 0x86, 0x93, 0x7c, 0x1b, 0x26, 0xf5, 0x88, 0x6c, 0x76, 0x24, 0x22, 0x5d, 0xca, 0x83, 0xa0, 0x72, 0x4b, 0x90, 0x82, 0x77, 0xca, 0xe1, 0x8d, 0x4e, 0xfa, 0x47, 0xae, 0xab, 0x19, 0x1b, 0x16, 0x9c, 0x57, 0xd6, 0xb, 0x28, 0xc, 0x91, 0x3, 0x91, 0x80, 0x5e, 0x0, 0x1a, 0xbc, 0x42, 0x7b, 0x53, 0x8f, 0x79, 0xe9, 0xd2, 0x14, 0x49, 0xb1, 0x40, 0xe9, 0xe7, 0x8a, 0x6c, 0x1, 0xbb, 0x4, 0x54, 0x54, 0x81, 0xdd, 0x9, 0xd6, 0xdc, 0x31, 0xe1, 0x7d, 0x1a, 0xe7, 0x49, 0x1f, 0x47, 0x11, 0xb6, 0x80, 0x0, 0x8a, 0x9f, 0x11, 0xf3, 0x2e, 0xf7, 0xed, 0x7, 0x80, 0xb9, 0xad, 0x52, 0xe2, 0x3, 0x31, 0x3d, 0x80, 0x64, 0xf, 0x0, 0xb0, 0x97, 0x37, 0xbf, 0xac, 0xfc, 0xc6, 0x36, 0xde, 0x5b, 0x54, 0xd6, 0x73, 0xb5, 0x69, 0x95, 0x6c, 0xb0, 0x2e, 0xe7, 0x1d, 0x82, 0x1d, 0xee, 0xdb, 0xd7, 0x91, 0x40, 0xf2, 0x3d, 0x1f, 0xaf, 0x3e, 0x46, 0x8b, 0x5b, 0x7d, 0x2e, 0x66, 0x4c, 0xa, 0xa1, 0xb5, 0x29, 0xc1, 0xa2, 0x8, 0xc3, 0xb4, 0xe3, 0x38, 0x4d, 0xd5, 0x94, 0x27, 0x7a, 0x8, 0x10, 0x7a, 0xe6, 0xed, 0xcb, 0xc5, 0x63, 0x55, 0xa5, 0x3d, 0x74, 0xb9, 0xf8, 0x61, 0x2c, 0xf0, 0x75, 0x10, 0x89, 0xbb, 0x73, 0xd7, 0x15, 0xd0, 0x8, 0x84, 0x65, 0x68, 0x51, 0x4d, 0x8, 0xf, 0x6d, 0x49, 0x20, 0x7f, 0x97, 0x1f, 0x2d, 0x53, 0x81, 0x3c, 0x56, 0x29, 0x44, 0x32, 0x0, 0x8d, 0x69, 0x90, 0x6b, 0xbd, 0x1d, 0x2c, 0x3b, 0x60, 0x1a, 0x33, 0xe7, 0xf2, 0x47, 0xf3, 0x6, 0xc0, 0x8f, 0x34, 0x3, 0x10, 0x70, 0x9, 0xbe, 0x2f, 0x69, 0xbf, 0x4, 0x51, 0x9b, 0xaa, 0x56, 0x9c, 0x4c, 0x68, 0x9f, 0xc0, 0x72, 0xe3, 0x15, 0x4b, 0xbe, 0xf3, 0x1a, 0x3, 0x41, 0xf2, 0x4d, 0x48, 0x1f, 0x69, 0x6, 0x10, 0xe0, 0xa, 0xfc, 0xb0, 0xd0, 0xc1, 0x27, 0x6a, 0xfc, 0x8f, 0xc3, 0x1d, 0xc3, 0x69, 0xe5, 0xce, 0x2f, 0xaa, 0xd6, 0x1b, 0x37, 0x46, 0xb, 0xc8, 0xa9, 0x7a, 0x68, 0xd5, 0xfb, 0xc7, 0xa, 0xf8, 0x8c, 0x7e, 0x9f, 0xb, 0x63, 0xc9, 0xef, 0xe5, 0x2f, 0xa2, 0xfb, 0xe8, 0xea, 0x0, 0x7e, 0x3d, 0x73, 0xf5, 0x67, 0xcc, 0x6f, 0x14, 0xc4, 0x31, 0x41, 0x58, 0xbe, 0xaa, 0xe, 0x71, 0xd1, 0x43, 0x0, 0x0, 0x5, 0xd4, 0x8a, 0xcc, 0x3b, 0x1c, 0xa8, 0x88, 0xf7, 0x25, 0xca, 0xcf, 0xf0, 0x4d, 0x78, 0x55, 0x2c, 0x1, 0xe0, 0x0, 0xd0, 0x54, 0x25, 0xcc, 0xec, 0x2e, 0x40, 0xd2, 0x11, 0xb0, 0x77, 0x69, 0x7a, 0x85, 0x9, 0x1c, 0x77, 0xf, 0xd0, 0x3f, 0x5b, 0x43, 0x82, 0x6b, 0xad, 0xad, 0xca, 0xb4, 0x1c, 0xad, 0x5, 0x0, 0x2d, 0x79, 0xcb, 0x5e, 0x39, 0x83, 0x45, 0x2d, 0x4d, 0xb6, 0x90, 0x91, 0x8, 0xf4, 0x21, 0xf9, 0x91, 0x67, 0x0, 0x92, 0xfb, 0xb4, 0x6d, 0xb9, 0x0, 0xdb, 0x78, 0xaf, 0x6c, 0xb5, 0x64, 0x88, 0x1e, 0x2, 0xc8, 0xf, 0x19, 0xd8, 0xaa, 0xb, 0x8e, 0xef, 0x8, 0xfa, 0xe8, 0xfc, 0x26, 0x56, 0x5e, 0x9c, 0x10, 0x8f, 0x94, 0xdd, 0x46, 0xa5, 0x88, 0x9f, 0x7c, 0x63, 0xa6, 0x9f, 0xcf, 0x81, 0x0, 0x1a, 0x13, 0xc1, 0x74, 0xdd, 0x82, 0xf2, 0x7b, 0xa9, 0x28, 0x68, 0x5f, 0xc5, 0x50, 0xac, 0x71, 0x17, 0xf5, 0x94, 0x2c, 0x5a, 0x73, 0xe, 0x66, 0x43, 0x1b, 0x74, 0x55, 0x4, 0x91, 0x42, 0x20, 0x61, 0xde, 0x20, 0xb8, 0x85, 0xe1, 0x15, 0x27, 0x40, 0x91, 0x5c, 0x10, 0xb2, 0x7, 0x15, 0xcb, 0x9, 0xa5, 0x64, 0xc2, 0xe2, 0x97, 0x21, 0x9b, 0xa2, 0xa4, 0x8f, 0xeb, 0x23, 0xcf, 0x0, 0xec, 0x5, 0x20, 0xd8, 0x53, 0x0, 0x28, 0x25, 0xf4, 0x15, 0xc, 0x97, 0x21, 0x9c, 0x7, 0x32, 0x9a, 0x2f, 0x48, 0x12, 0xcc, 0x0, 0xa8, 0xea, 0xd0, 0xa9, 0x18, 0x4b, 0xa0, 0xde, 0x2, 0xd9, 0x82, 0x60, 0xc1, 0x81, 0xc7, 0xb6, 0x8, 0x76, 0x44, 0x60, 0x3b, 0xfc, 0xf9, 0x5a, 0x40, 0x13, 0xeb, 0x68, 0x3, 0xeb, 0xa4, 0x61, 0xf0, 0xd4, 0x5f, 0xed, 0x6b, 0xad, 0x7b, 0x8a, 0x8f, 0x57, 0x26, 0x85, 0x3b, 0x49, 0x7b, 0x3, 0xfa, 0xe6, 0xe0, 0x8b, 0x7f, 0x51, 0xb1, 0x56, 0x17, 0xed, 0x26, 0xe4, 0xa4, 0x1e, 0x4, 0x9d, 0x6f, 0xd3, 0x38, 0xcf, 0x6, 0x3e, 0x5a, 0xde, 0xaf, 0xaf, 0xaf, 0x3, 0x40, 0xfe, 0x8, 0xc7, 0x3c, 0x37, 0x16, 0x34, 0xee, 0x98, 0x6e, 0x20, 0x1c, 0x3f, 0xb0, 0x68, 0x41, 0xa2, 0x31, 0x5, 0x61, 0x1f, 0x45, 0x20, 0x4d, 0x2f, 0x40, 0xa7, 0xe5, 0x10, 0x17, 0x65, 0xd5, 0xcd, 0xbd, 0x63, 0x99, 0x9e, 0x34, 0x7f, 0x43, 0x43, 0xc8, 0xba, 0x97, 0x3, 0x7c, 0x7a, 0xf0, 0x25, 0xf7, 0xfd, 0xf7, 0xe0, 0xa9, 0xf, 0xec, 0xdd, 0xe6, 0x32, 0xc5, 0xbd, 0x18, 0x3c, 0xf5, 0x57, 0xdd, 0x1, 0xf6, 0x85, 0xec, 0x41, 0x20, 0x7a, 0x43, 0x38, 0x4, 0xd0, 0x27, 0x25, 0x7b, 0x24, 0x81, 0x81, 0xf0, 0xe7, 0xa, 0x80, 0xf0, 0x36, 0xaf, 0x35, 0x51, 0xcf, 0x3f, 0xf, 0x32, 0xf5, 0x75, 0xe3, 0xa4, 0x6f, 0x5c, 0x75, 0xf3, 0xbf, 0xb6, 0xf9, 0x4, 0xae, 0xdd, 0x5, 0x86, 0x5c, 0xfa, 0xc0, 0x30, 0x9b, 0x6d, 0xf9, 0xd, 0x80, 0xbd, 0x93, 0x30, 0x2, 0x85, 0xf6, 0xc7, 0x9, 0x6f, 0xba, 0xb9, 0x85, 0x34, 0x2b, 0x5, 0x3e, 0x44, 0x63, 0xee, 0x5c, 0x7d, 0xeb, 0x59, 0x25, 0xf, 0x9f, 0x6a, 0x73, 0x29, 0x86, 0x5d, 0xfe, 0x70, 0xf, 0x6b, 0xdd, 0x7d, 0x65, 0xed, 0x1, 0x34, 0x66, 0x7f, 0x59, 0xdb, 0xb, 0x72, 0xaf, 0x96, 0x6c, 0xff, 0xd6, 0x66, 0xcd, 0x1a, 0x2a, 0x44, 0xbe, 0xe9, 0x38, 0xd, 0x13, 0x56, 0xdd, 0x32, 0xf6, 0x85, 0x24, 0xd2, 0xac, 0x27, 0x18, 0x3c, 0xf5, 0x57, 0x23, 0xe5, 0x66, 0x5e, 0xf4, 0x77, 0x15, 0x49, 0x62, 0x15, 0x7d, 0xab, 0xa3, 0x66, 0x82, 0xd3, 0xc7, 0x4, 0x6c, 0x26, 0xcd, 0xb, 0x34, 0xce, 0x42, 0xc7, 0x98, 0xfb, 0x56, 0xde, 0x3c, 0x36, 0x72, 0x99, 0x78, 0x9b, 0x3d, 0x82, 0x56, 0xce, 0x3d, 0x63, 0x2b, 0xbc, 0x7d, 0x74, 0x5e, 0xb, 0x55, 0x68, 0x20, 0x2d, 0xfa, 0x79, 0x25, 0x49, 0x64, 0xd3, 0x2c, 0x1, 0xe8, 0x63, 0xa5, 0x83, 0x6a, 0xc5, 0x4c, 0xbd, 0xc1, 0xd0, 0xcb, 0x16, 0xef, 0x67, 0x6d, 0x76, 0x9c, 0x3f, 0xcc, 0x55, 0x61, 0x62, 0x8f, 0xd4, 0xc6, 0xb, 0xf4, 0xe8, 0xd0, 0xbe, 0x8c, 0x22, 0xb0, 0x2f, 0xa0, 0x33, 0x61, 0xdd, 0xc1, 0x2e, 0x30, 0x74, 0xf0, 0xd4, 0x7, 0x17, 0xac, 0xbe, 0x75, 0xec, 0xba, 0xe2, 0x4, 0xaa, 0x6e, 0xb9, 0xc3, 0xa6, 0x3f, 0x96, 0x32, 0x34, 0xbf, 0xf7, 0x48, 0xef, 0x2f, 0x45, 0xaa, 0x68, 0xf6, 0x37, 0x34, 0xce, 0xf9, 0xaa, 0x65, 0xde, 0xb1, 0x44, 0x9f, 0x19, 0x7a, 0xd9, 0x83, 0xfb, 0x77, 0x14, 0x71, 0x3a, 0x6, 0x34, 0x0, 0xd6, 0x9d, 0xee, 0xf, 0x94, 0x2c, 0xa8, 0x7b, 0x9b, 0xfc, 0x44, 0x59, 0x6, 0x87, 0x41, 0xfb, 0xcb, 0x9d, 0x5b, 0x24, 0x6f, 0x22, 0x4f, 0x9f, 0x80, 0xec, 0x38, 0x52, 0x7d, 0xa2, 0x4a, 0x55, 0x35, 0x3, 0xac, 0x9c, 0xf3, 0xf9, 0xac, 0x80, 0x17, 0xe0, 0x9f, 0xa3, 0x5a, 0x9d, 0xdc, 0x9f, 0x2f, 0xb7, 0xa7, 0x6d, 0x4, 0x9a, 0x86, 0xbd, 0x40, 0xc2, 0x88, 0xce, 0x26, 0x59, 0x92, 0x20, 0xd9, 0x43, 0x1, 0xec, 0x7, 0x50, 0x85, 0xcb, 0x3b, 0xc2, 0x78, 0xa8, 0x1c, 0x87, 0x28, 0xfe, 0xb6, 0x50, 0xbb, 0xa0, 0xdf, 0xad, 0xbe, 0x4a, 0xe3, 0x6c, 0x8c, 0x2a, 0x57, 0x4d, 0x63, 0xf7, 0x1f, 0x6e, 0x19, 0xfb, 0x2a, 0x8d, 0x79, 0x33, 0x98, 0x32, 0x8e, 0x76, 0x74, 0x8d, 0x9a, 0xf4, 0x88, 0xb3, 0x6d, 0x53, 0x0, 0x5, 0xe9, 0xe3, 0xb2, 0xf6, 0xe4, 0x21, 0x97, 0x2d, 0xee, 0xd9, 0xb9, 0x64, 0x4b, 0x6, 0x6, 0x5f, 0xfa, 0xe0, 0x60, 0x59, 0x4d, 0xf6, 0x55, 0xdd, 0xdc, 0x36, 0x37, 0x49, 0x40, 0xac, 0xd7, 0x40, 0xce, 0x96, 0xf, 0x10, 0xdc, 0xd2, 0xd0, 0xfd, 0x63, 0xaf, 0x47, 0xc5, 0xab, 0x89, 0x1, 0x6, 0x5f, 0x72, 0x9f, 0x1, 0xcc, 0x3, 0x2, 0x32, 0x2, 0x90, 0x53, 0xe5, 0xa, 0x2e, 0x14, 0x75, 0x61, 0xc5, 0x5d, 0x5f, 0x41, 0x78, 0x9e, 0x71, 0xa1, 0xf1, 0xb2, 0xf6, 0xd4, 0xc4, 0xa9, 0xd1, 0x9, 0x40, 0xe0, 0xc, 0xc0, 0x8e, 0xf1, 0x1c, 0xab, 0x0, 0x76, 0xc0, 0x89, 0x68, 0x34, 0x39, 0x5f, 0x3e, 0x90, 0x66, 0x57, 0xf1, 0x9, 0xa6, 0x1, 0xd4, 0xc4, 0x0, 0xd, 0x3d, 0x7a, 0xb, 0x34, 0xf7, 0x4a, 0xc8, 0xfa, 0x54, 0x8c, 0x98, 0x19, 0x2a, 0x35, 0x25, 0x5c, 0x1c, 0xb7, 0x60, 0x37, 0xb0, 0x83, 0x49, 0x9d, 0x3d, 0xe4, 0xd2, 0x87, 0x7a, 0x77, 0x36, 0x1, 0x6b, 0x81, 0x21, 0x97, 0x3e, 0x74, 0x2, 0x64, 0xc7, 0xe6, 0x25, 0xff, 0xf0, 0xa, 0xea, 0xb6, 0xe0, 0xaa, 0x54, 0x58, 0xdc, 0xb7, 0x61, 0x13, 0x2d, 0x77, 0xd1, 0x98, 0x57, 0xe2, 0xca, 0x57, 0x13, 0x3, 0x2c, 0xfb, 0xe9, 0xb1, 0xa2, 0x71, 0x56, 0x80, 0x39, 0x37, 0x2e, 0x86, 0x57, 0xf8, 0xb4, 0x86, 0x52, 0x52, 0x8e, 0xf7, 0x3e, 0xef, 0x2e, 0x45, 0x48, 0x1a, 0x7, 0xe0, 0x92, 0x76, 0xa0, 0x4b, 0x87, 0xc0, 0xb0, 0x69, 0x8f, 0xf4, 0x4, 0xf4, 0x45, 0x49, 0x47, 0x7b, 0x92, 0xbf, 0x9, 0x26, 0x4f, 0x22, 0xf0, 0x11, 0x87, 0xab, 0x52, 0x61, 0xb1, 0xd, 0xcb, 0x77, 0x15, 0x20, 0x40, 0x2e, 0x91, 0x70, 0x7f, 0x5c, 0x19, 0x6b, 0xd6, 0xdf, 0x57, 0xcd, 0x3d, 0x7d, 0xab, 0x31, 0xce, 0x8b, 0x20, 0x4b, 0xc, 0x3, 0x95, 0x76, 0x5f, 0x26, 0x30, 0x3e, 0x76, 0x87, 0xec, 0x85, 0x43, 0x2e, 0x7d, 0x68, 0x6c, 0x47, 0x10, 0x2c, 0x69, 0x90, 0xb5, 0x57, 0x49, 0xf6, 0xdc, 0xd0, 0xce, 0x1d, 0x55, 0xe2, 0x4, 0x95, 0xc6, 0xb, 0xe5, 0x65, 0x56, 0xff, 0xe1, 0x96, 0x33, 0x63, 0xd7, 0x59, 0x24, 0x62, 0xc0, 0x31, 0x4e, 0xea, 0x1a, 0x0, 0x9b, 0xdb, 0xd2, 0xc9, 0xc7, 0x89, 0x7f, 0x45, 0xcf, 0x7e, 0x55, 0x68, 0x25, 0xdb, 0x1f, 0xd0, 0x15, 0xc3, 0xa7, 0x3f, 0xba, 0x17, 0x76, 0x23, 0x18, 0x7a, 0xf9, 0xc3, 0x17, 0xc9, 0xba, 0x97, 0x90, 0xec, 0xe6, 0x1f, 0x22, 0xdb, 0xca, 0x15, 0x24, 0xa, 0x3f, 0xa5, 0xde, 0xb0, 0x8d, 0xf1, 0x82, 0x85, 0x96, 0xfe, 0x53, 0xc9, 0x35, 0x16, 0xc9, 0x30, 0x80, 0x49, 0xfd, 0x15, 0x60, 0x16, 0x2c, 0xa7, 0xb4, 0xb4, 0x56, 0x78, 0xe2, 0x9e, 0x7d, 0x7f, 0x44, 0x43, 0x1a, 0x40, 0xfa, 0xbc, 0xeb, 0x66, 0xef, 0x4e, 0x88, 0x36, 0xed, 0xe, 0xc3, 0xa6, 0x3d, 0x72, 0x34, 0x64, 0xaf, 0x4, 0xd1, 0x97, 0x9e, 0xda, 0xc7, 0xb0, 0x7a, 0x86, 0x12, 0xf8, 0x29, 0x3b, 0x61, 0x56, 0x3e, 0x9e, 0x48, 0xe3, 0xef, 0x4b, 0x65, 0x5e, 0x26, 0xcd, 0xba, 0x52, 0x65, 0xad, 0x99, 0x1, 0x86, 0x5f, 0xf1, 0x44, 0xca, 0x75, 0x33, 0x17, 0x93, 0xdc, 0xb3, 0x60, 0x82, 0xa3, 0xc6, 0x8b, 0x86, 0x14, 0x4, 0x1a, 0x7a, 0x9b, 0x2b, 0xc9, 0x9e, 0x36, 0xf4, 0xf2, 0xc5, 0x8b, 0x3b, 0x9b, 0xb8, 0xe5, 0x60, 0xd8, 0xb4, 0x47, 0x87, 0x59, 0xd7, 0x9d, 0x2b, 0xd9, 0x91, 0xb9, 0x63, 0x71, 0x88, 0xa, 0x8d, 0x64, 0x85, 0x97, 0x2, 0xa5, 0x2e, 0x3e, 0x9d, 0x70, 0x78, 0x4e, 0x93, 0x22, 0x9d, 0x7b, 0xff, 0x70, 0xf3, 0xbf, 0xfe, 0xb2, 0x54, 0x79, 0x6b, 0x62, 0x80, 0x61, 0x57, 0x3c, 0x9e, 0x92, 0xcd, 0x5c, 0x27, 0xeb, 0xde, 0x0, 0xa0, 0x67, 0xc1, 0x92, 0xe4, 0x4, 0x2e, 0x7f, 0xdf, 0x3b, 0xfa, 0x27, 0x8c, 0xc1, 0xba, 0xf6, 0xb3, 0x43, 0x2e, 0x5b, 0x7c, 0x7f, 0x65, 0xa5, 0xec, 0x38, 0x18, 0x7a, 0xf9, 0xa3, 0x43, 0x25, 0xf7, 0x2e, 0x40, 0x47, 0xfa, 0x36, 0xd, 0x86, 0x7e, 0x6b, 0xc1, 0x3, 0xbd, 0x65, 0xfe, 0x8, 0x7e, 0xc3, 0x57, 0xee, 0x14, 0x94, 0xf0, 0x5, 0xc0, 0xa5, 0xe1, 0xdf, 0xca, 0x95, 0xb9, 0x6a, 0x6, 0x18, 0x7a, 0xf9, 0xa3, 0xd, 0x72, 0x33, 0xdf, 0xb3, 0xae, 0xfb, 0xdf, 0x0, 0xd3, 0x5e, 0x68, 0xb4, 0x64, 0x5f, 0x9d, 0x2a, 0xd3, 0x3a, 0xdc, 0x18, 0xa6, 0x21, 0x9d, 0x39, 0xe4, 0xd2, 0x87, 0x7e, 0x3b, 0xf4, 0xf2, 0x47, 0x92, 0xde, 0x95, 0xac, 0x26, 0x18, 0x36, 0xed, 0xb1, 0x93, 0xad, 0xcd, 0x3c, 0x28, 0xab, 0xc3, 0xa, 0xd7, 0x52, 0x94, 0xd2, 0x7a, 0xe2, 0x70, 0x81, 0xd6, 0x71, 0xf3, 0x9a, 0xdd, 0x2e, 0x92, 0x1b, 0x40, 0xf3, 0xa6, 0x84, 0x5d, 0xa, 0xb6, 0x53, 0x4, 0x43, 0x62, 0x86, 0x27, 0xfc, 0x9, 0x6c, 0x75, 0x68, 0x67, 0x31, 0x54, 0xc5, 0x0, 0xc3, 0xaf, 0x78, 0xbc, 0x1b, 0xa0, 0x1f, 0x59, 0x6b, 0xaf, 0x3, 0xe1, 0xd0, 0x10, 0xc1, 0x21, 0xc7, 0x85, 0x2b, 0xc7, 0xa2, 0x18, 0x9f, 0x45, 0xe1, 0xcc, 0x8b, 0x2b, 0x5, 0x78, 0x8, 0xb8, 0x9f, 0xa1, 0x74, 0x9, 0x10, 0x46, 0xd0, 0xd1, 0xd6, 0xba, 0x2f, 0xf, 0xb9, 0xfc, 0x91, 0xe3, 0x3a, 0x8c, 0xc2, 0x25, 0x60, 0xc8, 0x65, 0xf, 0xff, 0xa7, 0xb5, 0xd9, 0x45, 0x24, 0xf, 0xa, 0xd4, 0x6f, 0x1a, 0xe3, 0xf9, 0x39, 0x17, 0x8a, 0x3f, 0x85, 0xf5, 0x46, 0xc, 0x2e, 0xa, 0xf1, 0x26, 0x3f, 0x2e, 0x7d, 0x7c, 0xac, 0x5c, 0x75, 0xf3, 0x99, 0x3, 0x8d, 0x93, 0xfe, 0x94, 0x49, 0x35, 0xee, 0x63, 0x52, 0x4d, 0x4d, 0x26, 0x95, 0xee, 0x63, 0x9c, 0xf4, 0x20, 0x3a, 0xe9, 0xd1, 0x74, 0xd2, 0x67, 0xd1, 0x38, 0x5f, 0xa6, 0x71, 0xbe, 0x44, 0xf0, 0xd9, 0x72, 0x65, 0x2f, 0x27, 0xb8, 0x47, 0x10, 0xff, 0xd7, 0x7b, 0xca, 0xda, 0xaf, 0x4a, 0xf6, 0x3b, 0x8, 0x56, 0xa3, 0xb0, 0xd2, 0x95, 0xae, 0x55, 0x2d, 0x8c, 0xf5, 0xd8, 0x5b, 0x62, 0xc8, 0xb9, 0x70, 0x2b, 0x68, 0x1e, 0x4b, 0x77, 0xdb, 0xe7, 0xbc, 0x17, 0x67, 0x1c, 0xd3, 0xe1, 0x9b, 0x48, 0xe, 0xbf, 0xe2, 0x89, 0x46, 0xeb, 0x66, 0xd6, 0xca, 0xba, 0x7, 0x91, 0xdc, 0x23, 0x98, 0x92, 0x87, 0xc4, 0x44, 0x71, 0x12, 0x78, 0xe8, 0x92, 0xab, 0x40, 0xe7, 0xe4, 0x95, 0x73, 0x3e, 0xf7, 0x7e, 0x71, 0x94, 0xa3, 0xbe, 0xf2, 0x5b, 0xb6, 0x6c, 0x7b, 0x97, 0x4e, 0xba, 0x81, 0xb2, 0xd6, 0x48, 0xd6, 0x5d, 0x35, 0xf7, 0x8c, 0xb2, 0x47, 0xcc, 0x54, 0x44, 0x85, 0xa1, 0xd3, 0x1e, 0xed, 0xe, 0x60, 0x32, 0xac, 0x9d, 0x5, 0xf8, 0x67, 0xff, 0xa1, 0xe3, 0xf, 0x8c, 0x40, 0xe1, 0x3c, 0x78, 0xb, 0xc8, 0xb7, 0x4, 0xde, 0xea, 0x66, 0xb3, 0xd7, 0xaf, 0xb9, 0xf5, 0xcc, 0x4c, 0xd, 0x59, 0xb4, 0x9, 0x86, 0x5c, 0xfe, 0x48, 0x9a, 0xc0, 0x22, 0x12, 0x47, 0xc9, 0xda, 0xdc, 0x2c, 0x1b, 0x2b, 0x26, 0x7a, 0x25, 0x55, 0x46, 0x56, 0xe0, 0x5d, 0xab, 0xe6, 0x9e, 0x3e, 0x25, 0xc9, 0xba, 0x54, 0x54, 0xd8, 0x61, 0xd3, 0x1e, 0x3b, 0x49, 0x72, 0x7f, 0xe3, 0x89, 0xb5, 0x1e, 0x9b, 0xb7, 0x4a, 0x2d, 0xee, 0xec, 0x87, 0x70, 0xcc, 0xb8, 0xc5, 0x23, 0x2c, 0x8a, 0xab, 0xc8, 0x38, 0x91, 0xa7, 0x83, 0x49, 0xda, 0x4a, 0x9a, 0xd7, 0x40, 0x2e, 0x2, 0xcc, 0x4f, 0x56, 0xce, 0xf9, 0x6c, 0xe2, 0x1e, 0x45, 0xc3, 0xaf, 0x78, 0x7c, 0xa8, 0xb5, 0xee, 0xb5, 0xde, 0xde, 0x48, 0x3a, 0x88, 0xa4, 0xbf, 0x3f, 0x62, 0x20, 0xaa, 0x7b, 0xc7, 0x27, 0xb6, 0xaa, 0x53, 0xb8, 0x2e, 0x51, 0x38, 0x2a, 0xf6, 0xa4, 0x2e, 0xc4, 0x97, 0xbf, 0xf, 0x3, 0x9f, 0x4d, 0x35, 0x34, 0x7d, 0x6e, 0xd9, 0xcf, 0x4e, 0x4a, 0xf4, 0xc8, 0x98, 0x8a, 0xe, 0x8d, 0x12, 0xf0, 0x16, 0xc0, 0x55, 0x20, 0x86, 0x12, 0x85, 0x47, 0xb7, 0x14, 0x54, 0xa8, 0x18, 0xca, 0x59, 0x7f, 0xc2, 0x61, 0x2c, 0xfb, 0x5d, 0x1e, 0xb5, 0xb9, 0x19, 0x70, 0x89, 0x64, 0xf, 0xc9, 0xe, 0x84, 0x78, 0x10, 0xa9, 0xb, 0x87, 0x4e, 0x7b, 0x74, 0x29, 0xc0, 0xc5, 0x0, 0x9f, 0x58, 0x75, 0xd3, 0xe7, 0x3e, 0xac, 0x16, 0x41, 0x43, 0xa7, 0x3d, 0x76, 0x24, 0x81, 0x33, 0x41, 0x9c, 0x62, 0xdd, 0xec, 0x27, 0x1, 0xf4, 0x26, 0xd1, 0xe4, 0x17, 0x26, 0xe7, 0xa, 0x17, 0xb9, 0x8c, 0x92, 0x6d, 0xb8, 0x8f, 0x7f, 0xe, 0xed, 0xba, 0xc2, 0xd7, 0x68, 0xcc, 0x6d, 0x49, 0x13, 0x3f, 0x8e, 0x14, 0x25, 0x61, 0xd8, 0xf4, 0x5f, 0x9f, 0x2e, 0xeb, 0x3e, 0xc, 0x44, 0xad, 0x70, 0xed, 0x70, 0x28, 0x68, 0x57, 0xa1, 0x1d, 0x4b, 0x20, 0xa9, 0x19, 0xe4, 0x3f, 0x0, 0xbc, 0xb, 0xe0, 0x6d, 0x80, 0x7f, 0x34, 0xc6, 0xbc, 0x28, 0x61, 0x83, 0x64, 0x37, 0x1b, 0x63, 0xb6, 0x9, 0xcc, 0x2, 0x70, 0x8d, 0x31, 0x8e, 0x9b, 0xcd, 0x34, 0x2, 0xec, 0x66, 0xc, 0xf, 0x4, 0x74, 0x8c, 0xb5, 0x3a, 0xe, 0xd0, 0x1, 0x0, 0x7a, 0xc8, 0xda, 0xde, 0x24, 0xf7, 0xcb, 0x21, 0x2d, 0x7c, 0x42, 0xa, 0xa, 0xfb, 0xe9, 0x44, 0x2a, 0x15, 0x4a, 0x4f, 0x92, 0x5, 0xcd, 0x8d, 0x3d, 0xf7, 0xeb, 0x73, 0xd5, 0x33, 0xd7, 0xf, 0x4e, 0x5c, 0xce, 0xa9, 0xf8, 0xec, 0x60, 0x9, 0x2b, 0x40, 0xf3, 0x2, 0xa0, 0xe3, 0x2, 0x4f, 0x9e, 0xf0, 0x69, 0xd1, 0xed, 0xf, 0x5, 0x63, 0x49, 0xee, 0xc1, 0x3b, 0x69, 0x3b, 0x68, 0x3a, 0x10, 0xd, 0x1b, 0x25, 0x1c, 0x2, 0xe0, 0x10, 0x3f, 0xc6, 0x9, 0x92, 0x1d, 0x27, 0xe1, 0x7d, 0x48, 0xcd, 0x90, 0xcd, 0x10, 0xb4, 0x0, 0xe4, 0x66, 0xb2, 0x24, 0xe9, 0x0, 0x4a, 0x59, 0xab, 0xee, 0x80, 0xf6, 0x7, 0x90, 0xf3, 0x48, 0xa, 0x16, 0xc9, 0xf8, 0xe, 0xd1, 0x2c, 0x76, 0xe7, 0xab, 0x9c, 0xf8, 0xc5, 0xe3, 0x40, 0x1, 0x7e, 0x45, 0x43, 0x4a, 0xb0, 0x0, 0xc, 0x69, 0x9e, 0x14, 0xf9, 0x83, 0xf6, 0x20, 0x3e, 0x50, 0x5, 0x3, 0x90, 0x7c, 0x17, 0xe4, 0x77, 0x64, 0xdd, 0x27, 0xdb, 0xeb, 0xb8, 0xb4, 0xa, 0x4a, 0x13, 0x2a, 0x57, 0x48, 0x81, 0x24, 0xc2, 0x4e, 0x97, 0xc1, 0x88, 0xda, 0x1d, 0x40, 0x5f, 0x40, 0x7d, 0xc1, 0x9c, 0x4a, 0x91, 0x4b, 0x26, 0x70, 0x79, 0xf, 0x8c, 0x6e, 0xc8, 0x7f, 0x9b, 0xcb, 0x28, 0xbf, 0x59, 0x46, 0xb2, 0x65, 0x2f, 0xc4, 0x6f, 0xee, 0x5c, 0x24, 0x43, 0x72, 0x9, 0x8d, 0xf3, 0x5f, 0x2b, 0x67, 0x9f, 0x5a, 0xd6, 0xa0, 0x53, 0x2d, 0x54, 0xdc, 0x85, 0xaf, 0x9c, 0xf3, 0x59, 0x57, 0x56, 0xcf, 0x1, 0xe6, 0x7, 0xcc, 0x4f, 0x3b, 0x29, 0xce, 0xae, 0xf, 0xc4, 0xdb, 0xb2, 0x11, 0x11, 0x16, 0xf5, 0x1e, 0x31, 0xdf, 0x44, 0x7d, 0xe7, 0x3f, 0x33, 0x58, 0x72, 0xe, 0x7f, 0x5b, 0x4e, 0x7f, 0x7e, 0x1, 0x86, 0x6, 0x86, 0x26, 0xbf, 0xb6, 0xda, 0xf, 0xf3, 0xe6, 0x1c, 0x20, 0xd2, 0x0, 0xfe, 0x7d, 0xb1, 0x52, 0x5e, 0x6c, 0xcf, 0x47, 0x4c, 0x19, 0x4b, 0xc5, 0x61, 0x89, 0xfa, 0x86, 0x5d, 0xb9, 0xe8, 0x19, 0x73, 0x1e, 0x58, 0x31, 0xfb, 0xd4, 0x76, 0xf5, 0x8e, 0xae, 0x6a, 0xc, 0x5f, 0x75, 0xd3, 0xe7, 0x32, 0xc6, 0x71, 0x66, 0xd0, 0x98, 0x3f, 0x7a, 0x1e, 0x81, 0xf0, 0x14, 0x83, 0xbc, 0xcd, 0x1a, 0xa0, 0x77, 0x94, 0x27, 0x3, 0x79, 0x3d, 0xf8, 0xf1, 0xc3, 0xe4, 0x1b, 0x11, 0x7c, 0xbb, 0x49, 0x7e, 0x4d, 0x8, 0xb, 0xc3, 0xa, 0xec, 0x61, 0x21, 0x3b, 0x49, 0x2e, 0x6e, 0x48, 0x78, 0x6c, 0x65, 0x53, 0x2a, 0x8e, 0x1b, 0x32, 0x52, 0x79, 0xc6, 0x2b, 0x22, 0xbc, 0xcd, 0xa1, 0x77, 0xa, 0x8b, 0x1f, 0x25, 0x7c, 0x5f, 0x9c, 0x6f, 0x51, 0x18, 0x23, 0xca, 0x15, 0x67, 0xe3, 0x29, 0x28, 0x77, 0x44, 0x1d, 0xa, 0x3b, 0x6, 0x56, 0xdc, 0x43, 0x77, 0x8, 0x3, 0x0, 0xc0, 0x8a, 0xd9, 0xa7, 0xbc, 0x9, 0x98, 0x6f, 0x48, 0xb0, 0xc1, 0x4a, 0x97, 0x90, 0xcd, 0x3a, 0xa8, 0x72, 0x40, 0x71, 0xff, 0x40, 0x54, 0x13, 0xd8, 0xaa, 0x3d, 0xd3, 0x5e, 0xb0, 0xb5, 0x1d, 0x22, 0x30, 0x57, 0x80, 0xc5, 0x8, 0xec, 0xc6, 0xc5, 0x47, 0x1b, 0xe2, 0x17, 0x7f, 0xc7, 0x32, 0xf9, 0x97, 0xcb, 0xaf, 0x5c, 0x1e, 0x95, 0x94, 0xc5, 0xbb, 0xf7, 0x27, 0xc0, 0x34, 0x61, 0xd8, 0x15, 0x4f, 0x8c, 0xa9, 0x4b, 0x6, 0x0, 0x80, 0x15, 0x37, 0x9e, 0xf2, 0x2b, 0x81, 0x73, 0xfd, 0x59, 0x89, 0xe0, 0xbc, 0xdb, 0x9c, 0xdf, 0xa3, 0x1f, 0x6, 0x1, 0xaf, 0xd0, 0x38, 0xf3, 0x40, 0xf3, 0x3d, 0x90, 0x8f, 0xa3, 0x50, 0x4b, 0x66, 0x72, 0x3e, 0x92, 0x9d, 0x24, 0x8a, 0x24, 0x8, 0x61, 0x1c, 0x2, 0x1a, 0xe, 0x60, 0xca, 0x51, 0x57, 0x3d, 0x9d, 0x6e, 0xaf, 0xfc, 0x6a, 0x56, 0xe3, 0x68, 0x9c, 0xaf, 0xd2, 0x38, 0xbf, 0x1, 0xe8, 0x2, 0xc, 0x66, 0xa5, 0x8, 0xf0, 0x3, 0x81, 0x4f, 0x1, 0x9c, 0x4d, 0x9a, 0xff, 0x30, 0xc6, 0x4c, 0x5f, 0x31, 0xfb, 0x94, 0x6f, 0x3, 0xe6, 0x6b, 0xa0, 0xf9, 0x6d, 0x28, 0xae, 0xef, 0x27, 0x17, 0xb5, 0x6d, 0x5a, 0xa5, 0x17, 0x6a, 0xfc, 0xbe, 0xf3, 0x2f, 0x7f, 0xd2, 0x43, 0x79, 0xbc, 0x68, 0x8c, 0x9b, 0x75, 0xcf, 0x6b, 0x2f, 0x6, 0x48, 0xa4, 0xc9, 0x8c, 0xb8, 0xf2, 0xa9, 0x23, 0x24, 0xfb, 0x6b, 0x49, 0x69, 0x49, 0x1b, 0xc, 0xb9, 0x95, 0xc6, 0xac, 0x0, 0x78, 0xf3, 0xf2, 0x59, 0x27, 0xbd, 0x19, 0x11, 0xff, 0x13, 0xd6, 0xba, 0xf3, 0x25, 0x9d, 0x60, 0x72, 0xaa, 0x5b, 0xe8, 0xa0, 0xe4, 0x92, 0xc5, 0x6d, 0x4b, 0x6f, 0xd1, 0xd6, 0x78, 0x51, 0x71, 0xc3, 0xcf, 0x95, 0xa4, 0x53, 0xd, 0x4, 0x55, 0x8d, 0xcd, 0x43, 0x5e, 0x2f, 0xca, 0x47, 0x68, 0xcc, 0xf8, 0x95, 0xb3, 0x4f, 0x2e, 0x77, 0xa, 0x7a, 0xd5, 0x25, 0xa8, 0x19, 0x46, 0x5c, 0xf9, 0xd4, 0xf5, 0x92, 0x5e, 0x7, 0x39, 0x6f, 0xc5, 0xac, 0x93, 0xca, 0x6e, 0xf5, 0x3a, 0xfc, 0x4b, 0x4f, 0x35, 0x42, 0xf6, 0x69, 0x41, 0xc7, 0x78, 0x2, 0x1e, 0x73, 0x15, 0x66, 0x58, 0xa7, 0x8b, 0x41, 0x51, 0x78, 0xc, 0x29, 0x45, 0x22, 0x96, 0xf9, 0xbe, 0x38, 0x4e, 0x71, 0xfc, 0x28, 0x4, 0x29, 0x26, 0x8d, 0xf6, 0x60, 0x15, 0x7f, 0x78, 0x7c, 0x97, 0x34, 0x3f, 0x58, 0x31, 0xfb, 0xe4, 0x1f, 0x26, 0x9d, 0x7e, 0xa7, 0xe, 0x9a, 0x23, 0xaf, 0x59, 0x4a, 0xb7, 0x65, 0xfb, 0xe3, 0x24, 0x8e, 0x6, 0xd0, 0xc3, 0xdb, 0x3c, 0x23, 0xd8, 0x87, 0x24, 0x8a, 0x5, 0x18, 0xf1, 0x5c, 0xe9, 0xe4, 0x43, 0x5c, 0xba, 0xa5, 0x58, 0x24, 0xa, 0x65, 0xc5, 0xdf, 0x95, 0xcb, 0x2f, 0xae, 0x4c, 0x8c, 0xf9, 0x46, 0xc1, 0x7a, 0x4b, 0xff, 0xe0, 0x68, 0x3e, 0x63, 0x85, 0xcf, 0xaf, 0xba, 0xf1, 0x94, 0x44, 0xcd, 0xc1, 0x9d, 0x6a, 0xca, 0x5d, 0xf6, 0xd3, 0x63, 0xb5, 0xf2, 0xc6, 0x53, 0x4f, 0x25, 0x53, 0xb7, 0x4a, 0x7c, 0xc3, 0x57, 0xd5, 0x19, 0xbd, 0xce, 0x90, 0x31, 0xcf, 0x61, 0x4d, 0xa1, 0x54, 0xdc, 0x8, 0xbd, 0x32, 0x36, 0x9d, 0xb8, 0x30, 0x96, 0xf8, 0xae, 0x5c, 0x7e, 0x71, 0x71, 0xe2, 0xbe, 0xf1, 0xe4, 0xe3, 0x90, 0x6e, 0x3b, 0x84, 0xe4, 0xb7, 0x92, 0xa6, 0x41, 0x67, 0xdb, 0xf2, 0x1, 0x0, 0xcb, 0x67, 0x8d, 0xb9, 0x16, 0x34, 0xd7, 0x4a, 0x58, 0xf, 0xef, 0x4, 0x92, 0x28, 0x4a, 0xed, 0x56, 0x57, 0xb0, 0xb7, 0x72, 0xb9, 0xb0, 0x52, 0xdf, 0x6, 0x2e, 0x71, 0x7e, 0xf8, 0x36, 0x0, 0x1f, 0x24, 0x8d, 0xfb, 0xba, 0xd2, 0x9b, 0x86, 0x7f, 0xe9, 0xe9, 0xde, 0xa0, 0x1e, 0x25, 0x30, 0x24, 0x5f, 0xbc, 0xb8, 0x3d, 0x74, 0x6b, 0x19, 0x71, 0x4b, 0xa5, 0xd1, 0x16, 0xa9, 0x22, 0x4e, 0x7a, 0xc8, 0x7d, 0x17, 0xac, 0x95, 0xf4, 0x67, 0x2d, 0x73, 0x5d, 0x9b, 0xef, 0xc9, 0x62, 0x5b, 0x4f, 0xa5, 0x7, 0xdd, 0xbe, 0xb7, 0x4b, 0x46, 0xfe, 0x5b, 0x0, 0x92, 0x5e, 0x27, 0xf9, 0xff, 0x56, 0xcc, 0x3e, 0x69, 0x66, 0xd2, 0x38, 0x6f, 0x77, 0x4b, 0x53, 0x25, 0x60, 0xc, 0xcf, 0x12, 0xb0, 0x7f, 0xa1, 0x46, 0x10, 0xc7, 0xa3, 0x49, 0xf0, 0x6e, 0xb5, 0x69, 0xb3, 0xd5, 0x73, 0x89, 0x19, 0x41, 0x17, 0x40, 0xd6, 0x6f, 0xdc, 0x8d, 0x3e, 0x81, 0x63, 0x36, 0x88, 0x28, 0xdc, 0x37, 0xc0, 0x8f, 0xf3, 0xf, 0xc7, 0x38, 0xd7, 0x2f, 0xfb, 0xd9, 0x89, 0xb3, 0xd1, 0xe, 0x50, 0x17, 0x43, 0x40, 0x0, 0x56, 0x9a, 0x2, 0xb0, 0x4f, 0xcc, 0xa0, 0x19, 0x46, 0x54, 0x95, 0x57, 0xf8, 0xfb, 0xa8, 0xb4, 0xa2, 0xde, 0x17, 0x43, 0x41, 0x9a, 0xf2, 0xaf, 0x90, 0x15, 0x34, 0x6c, 0x14, 0x33, 0x24, 0xcd, 0x12, 0x63, 0x9c, 0x13, 0x49, 0x73, 0xa9, 0xf2, 0x7b, 0x39, 0xd2, 0x5b, 0xff, 0x58, 0xd0, 0xe5, 0xe7, 0xbc, 0x87, 0x83, 0xd6, 0xef, 0xf9, 0xf4, 0xf3, 0xb2, 0xf6, 0x22, 0x3e, 0x50, 0x6f, 0x3d, 0x0, 0xb9, 0xb7, 0x7f, 0xab, 0xe8, 0xa6, 0x5f, 0x6b, 0xab, 0x2f, 0x47, 0xe4, 0x36, 0x30, 0x41, 0x99, 0x11, 0x42, 0xc1, 0xc6, 0xf7, 0xc4, 0x12, 0x49, 0xff, 0x43, 0xf2, 0xe9, 0x65, 0x33, 0x47, 0x67, 0x0, 0xfc, 0x6e, 0xe4, 0x55, 0xcf, 0xbe, 0x2d, 0x6b, 0x7f, 0x9, 0x61, 0xcf, 0x12, 0xbe, 0x23, 0x16, 0xa0, 0x1, 0xf9, 0x24, 0xc9, 0x6f, 0x2e, 0x9b, 0x39, 0x7a, 0x59, 0x72, 0x18, 0x6e, 0xd, 0x75, 0xd3, 0x3, 0xc, 0xbf, 0xf2, 0x99, 0x43, 0xad, 0xd4, 0xd8, 0x7a, 0x7d, 0x21, 0x4a, 0x48, 0xe2, 0x40, 0xbc, 0x74, 0x8f, 0x98, 0x6f, 0xa3, 0x24, 0x7d, 0xc4, 0xe4, 0x17, 0x91, 0x6, 0x10, 0x78, 0xfb, 0x22, 0x98, 0xb4, 0xb, 0xc9, 0x76, 0xa4, 0x31, 0xf, 0xd2, 0x38, 0xa3, 0x40, 0xf3, 0x79, 0x3a, 0xd, 0x4f, 0x2c, 0xff, 0xd9, 0x89, 0x39, 0x1f, 0xc5, 0x65, 0x33, 0x47, 0x3f, 0xe, 0x9a, 0xa1, 0x2, 0x7e, 0xed, 0x37, 0x79, 0x81, 0x94, 0x37, 0xc9, 0xc8, 0x60, 0x1, 0x89, 0x1, 0x79, 0x9b, 0x80, 0x69, 0xed, 0x4d, 0x7c, 0xa0, 0x9e, 0x7a, 0x0, 0xa2, 0x1f, 0xc1, 0xc6, 0xdc, 0x53, 0x4e, 0xb2, 0x2a, 0xd7, 0x52, 0x5b, 0x79, 0xd0, 0xf8, 0xdd, 0x27, 0x83, 0x77, 0x88, 0x77, 0x57, 0x60, 0x44, 0x7a, 0xe5, 0x7b, 0x89, 0xfc, 0x9c, 0x7d, 0xe0, 0xe, 0x61, 0xfe, 0xa, 0x70, 0x11, 0xa8, 0x5b, 0x1, 0xbc, 0xe, 0x68, 0xe7, 0xf2, 0x99, 0xa3, 0x23, 0x3d, 0x72, 0x97, 0xff, 0x6c, 0xf4, 0x9f, 0x47, 0x5c, 0xf5, 0xec, 0x64, 0x8, 0x93, 0x24, 0x3b, 0x23, 0xa8, 0x82, 0xef, 0xdc, 0x42, 0x80, 0x5f, 0x3, 0x79, 0xc7, 0x8a, 0x99, 0x27, 0xbc, 0xd7, 0x11, 0x68, 0xaf, 0x1f, 0x6, 0x0, 0x8e, 0x5, 0xd8, 0xc3, 0xf7, 0xc9, 0xa8, 0xc8, 0xc9, 0xc4, 0x9f, 0x7d, 0xa, 0x3c, 0x68, 0x2, 0x46, 0x0, 0x80, 0xd0, 0x92, 0x6c, 0x0, 0x21, 0x71, 0xbd, 0x48, 0x66, 0xab, 0xc6, 0x9b, 0x97, 0x0, 0xb2, 0x20, 0x7, 0x81, 0xdc, 0x22, 0x69, 0xeb, 0xf2, 0x99, 0xa3, 0xdb, 0xe4, 0x88, 0xba, 0x7c, 0xe6, 0xe8, 0x77, 0x46, 0x5e, 0xbd, 0x64, 0x2e, 0xc4, 0xc5, 0x90, 0x9e, 0x5, 0x70, 0xa0, 0xa4, 0x77, 0x40, 0x4e, 0x4, 0xf4, 0xc2, 0xa, 0x6f, 0xc8, 0xe8, 0x10, 0xa8, 0x1b, 0x35, 0x70, 0xf8, 0x97, 0x9f, 0x7d, 0x9e, 0xc4, 0x31, 0xbe, 0x27, 0x47, 0x5b, 0x9, 0x92, 0xd7, 0xbb, 0x3c, 0xcf, 0xd9, 0xdb, 0x1, 0x2c, 0x5, 0x70, 0x32, 0xa0, 0xb3, 0x51, 0x62, 0xdb, 0xd9, 0x90, 0xb7, 0x50, 0x45, 0x6e, 0xed, 0x45, 0xbd, 0x4d, 0x66, 0xf9, 0xcc, 0xd1, 0xd, 0x6d, 0xfa, 0x30, 0x6, 0x46, 0x5e, 0xbd, 0xe4, 0x50, 0x49, 0xc3, 0x0, 0xac, 0x5f, 0x3e, 0xf3, 0x84, 0xd, 0xed, 0x89, 0xe3, 0x48, 0x3c, 0x74, 0x74, 0x86, 0x71, 0x30, 0xe2, 0xaa, 0xe7, 0xde, 0x2, 0xd0, 0x2b, 0xbc, 0xdf, 0x50, 0x29, 0xa2, 0x14, 0xbf, 0x97, 0xf4, 0x17, 0x80, 0x13, 0x1, 0xac, 0xa1, 0x77, 0x9e, 0x71, 0x2f, 0x0, 0x7b, 0x4a, 0x6a, 0x80, 0x77, 0xbc, 0x7d, 0xf, 0x0, 0x9f, 0x2, 0xd4, 0x17, 0xc0, 0xbe, 0x0, 0x3e, 0x9, 0x78, 0x1b, 0x51, 0x55, 0xea, 0xcf, 0x1f, 0xc, 0x33, 0x92, 0x32, 0xa4, 0xe9, 0xb6, 0x7c, 0xe6, 0xf1, 0xd9, 0xb6, 0x7e, 0x1b, 0x5, 0x47, 0x7f, 0x65, 0x69, 0xea, 0xf7, 0x33, 0x8e, 0xad, 0x29, 0x8d, 0x6a, 0xa1, 0x6e, 0x86, 0x0, 0x82, 0x3f, 0x17, 0xf4, 0x15, 0xf9, 0xb, 0xa1, 0x3c, 0xbf, 0x48, 0x8f, 0x28, 0x11, 0x94, 0xf1, 0x37, 0x44, 0xf4, 0x5, 0x32, 0x6f, 0x14, 0xb8, 0xce, 0xcd, 0xba, 0x7f, 0x58, 0x39, 0xfb, 0x44, 0xb, 0x60, 0x93, 0x7f, 0xe5, 0x60, 0xc4, 0xd5, 0x2f, 0x90, 0xb0, 0x3d, 0x0, 0xee, 0x9, 0x8f, 0x21, 0xfa, 0xc8, 0x6a, 0x89, 0x31, 0x74, 0x0, 0x6f, 0x71, 0x65, 0x5, 0x83, 0x8e, 0x7f, 0xca, 0x6d, 0x32, 0xde, 0x81, 0x9d, 0x45, 0x7c, 0xa0, 0x8e, 0x18, 0x40, 0xc0, 0x8f, 0x48, 0xb3, 0x44, 0xb2, 0x97, 0x82, 0x3c, 0x2d, 0x24, 0x4, 0x14, 0xf4, 0xba, 0x7e, 0x80, 0xbf, 0xf, 0x1e, 0x20, 0x6b, 0x9, 0xe0, 0xfb, 0x12, 0x1f, 0xf5, 0x89, 0x1f, 0x9, 0xcb, 0x6f, 0x38, 0x4e, 0x0, 0xb6, 0xf8, 0x17, 0x46, 0x5c, 0xb5, 0xe4, 0x75, 0x92, 0xbf, 0x15, 0x70, 0x1c, 0x80, 0x22, 0x51, 0xa1, 0x75, 0xe1, 0x42, 0x5c, 0xa8, 0xd0, 0x9e, 0x8c, 0xdb, 0xea, 0xa8, 0x13, 0xad, 0xa, 0xea, 0x46, 0xd, 0x5c, 0x3e, 0xf3, 0xf8, 0xd7, 0x97, 0xdd, 0x30, 0xea, 0x3e, 0x80, 0xd7, 0x1, 0xbc, 0x58, 0xc2, 0xaf, 0x3, 0xe7, 0x92, 0x22, 0x87, 0x11, 0x92, 0x46, 0xa1, 0x77, 0xbf, 0x7, 0xf8, 0xbf, 0xcb, 0x67, 0x1e, 0x5f, 0xd1, 0x59, 0x43, 0xc6, 0x18, 0xd2, 0x98, 0xbb, 0x42, 0xf6, 0xf9, 0x78, 0x3b, 0x7d, 0xa1, 0x4b, 0x97, 0x6f, 0xac, 0x81, 0x5, 0xf8, 0x1a, 0xda, 0xd7, 0x61, 0xa0, 0xdd, 0xa1, 0x6e, 0xd9, 0x77, 0xc4, 0x55, 0x4b, 0x8e, 0x4, 0x30, 0x82, 0xe4, 0x89, 0x0, 0xc6, 0x1, 0x8, 0x6f, 0x11, 0x13, 0x8c, 0xc1, 0x9b, 0x1, 0x9c, 0xb5, 0x7c, 0xe6, 0xf1, 0x4b, 0xaa, 0xc9, 0x63, 0xe4, 0xd5, 0x2f, 0x1c, 0x28, 0xd9, 0x4d, 0xfe, 0xb9, 0x88, 0xa5, 0x5c, 0x0, 0xa, 0xc0, 0xd7, 0x30, 0x32, 0x92, 0x16, 0x1, 0x98, 0xbc, 0x7c, 0xe6, 0xf1, 0x65, 0x17, 0x61, 0xd6, 0x2b, 0xd4, 0x2d, 0x3, 0x4, 0x30, 0xe2, 0xea, 0xe7, 0xfb, 0x0, 0x38, 0x3, 0xc0, 0xbf, 0x10, 0x3c, 0xa, 0xc0, 0x18, 0xdf, 0x4a, 0xb0, 0x43, 0xd0, 0x95, 0xcb, 0x6f, 0x18, 0x75, 0x47, 0x8d, 0xe9, 0x3f, 0x65, 0x68, 0x8e, 0x87, 0x77, 0xf6, 0x9e, 0xe7, 0x25, 0x1c, 0xc3, 0xa, 0xb2, 0x5, 0xea, 0xc2, 0x2e, 0x2b, 0xfb, 0x9f, 0x0, 0x7e, 0xb8, 0xfc, 0x86, 0x51, 0x5d, 0xc, 0xd0, 0x11, 0x70, 0xd4, 0x57, 0x96, 0xe, 0x7, 0x30, 0x9, 0x5e, 0x6f, 0xb0, 0xe9, 0xc5, 0x19, 0xc7, 0x7e, 0xb7, 0x96, 0xf4, 0x46, 0x5e, 0xb3, 0x94, 0x92, 0x3d, 0x95, 0xe4, 0xaf, 0x0, 0xec, 0x11, 0xee, 0x5, 0x22, 0xb4, 0x90, 0x9c, 0xfc, 0xe1, 0x3f, 0x5b, 0x0, 0x63, 0x49, 0xf3, 0x48, 0x67, 0x2c, 0x4d, 0x4f, 0xa, 0x76, 0x2b, 0x6, 0x68, 0xf, 0x18, 0x79, 0xcd, 0xd2, 0x3d, 0x24, 0xbd, 0x4c, 0xf2, 0xc0, 0x62, 0xb5, 0x33, 0xce, 0x5b, 0x59, 0xd2, 0x26, 0x92, 0x2f, 0x91, 0xbc, 0xf8, 0xc5, 0x19, 0xc7, 0x6c, 0x6a, 0x4b, 0x3e, 0xf5, 0xa, 0xff, 0xf4, 0xc, 0x0, 0x0, 0x23, 0xaf, 0x7e, 0xe1, 0x7a, 0x1a, 0x5e, 0x7, 0x20, 0xe5, 0xfb, 0x61, 0x65, 0x0, 0xec, 0x82, 0xb0, 0xb, 0xc4, 0xe, 0x0, 0x5b, 0x24, 0x6d, 0x26, 0xb9, 0xd, 0xc2, 0x66, 0x41, 0x4b, 0x8c, 0x31, 0xbf, 0xfb, 0xfd, 0x4f, 0x8e, 0x59, 0x57, 0x63, 0xd6, 0x9d, 0xe, 0x75, 0xa3, 0x6, 0x76, 0x26, 0xd0, 0x98, 0xff, 0x91, 0x74, 0x8, 0x80, 0x1e, 0x24, 0xb7, 0xc0, 0xdb, 0x5b, 0x6f, 0x33, 0xd, 0xdf, 0x3, 0xf0, 0xb6, 0xa4, 0x57, 0x1, 0xbc, 0xf4, 0xe2, 0x8c, 0x63, 0xdf, 0xe9, 0xec, 0xb2, 0x76, 0x41, 0x17, 0x74, 0x41, 0x17, 0x74, 0x41, 0x17, 0x74, 0x41, 0x17, 0x74, 0x41, 0x17, 0x74, 0x41, 0x17, 0xd4, 0x6, 0xff, 0x1f, 0x32, 0x68, 0x5, 0x7e, 0x99, 0xd5, 0x97, 0x49, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc3, 0x3e, 0x61, 0xcb, 0x0, 0x0, 0x19, 0xdb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x99, 0x3, 0x94, 0x7b, 0x39, 0x14, 0xc6, 0xd7, 0xb6, 0xed, 0x87, 0x62, 0x6d, 0xdb, 0x33, 0xe5, 0x6b, 0xc7, 0x6b, 0xdb, 0xb6, 0x77, 0x5c, 0xb7, 0x6b, 0xdb, 0xb6, 0x6d, 0xdb, 0xb6, 0xbd, 0xdb, 0x74, 0xbf, 0xde, 0xe6, 0x35, 0xff, 0xe6, 0xcc, 0xdb, 0x1d, 0x9f, 0x37, 0xd3, 0xe4, 0x9c, 0xdf, 0x49, 0x5e, 0x9c, 0x7c, 0x37, 0x37, 0x39, 0xed, 0x54, 0x2a, 0xa8, 0xa0, 0x82, 0xa, 0x2a, 0xa8, 0xa0, 0x82, 0xa, 0x2a, 0xa8, 0xa0, 0x82, 0xa, 0xf5, 0x17, 0x8c, 0x48, 0x6a, 0xe, 0x23, 0xd4, 0x73, 0xa5, 0x16, 0xe8, 0xba, 0x5f, 0xb, 0x74, 0xdf, 0xac, 0x87, 0x7a, 0x2f, 0x34, 0xc3, 0xfd, 0x9d, 0x66, 0x24, 0x71, 0x0, 0xca, 0xa2, 0x66, 0x34, 0xb3, 0xba, 0x37, 0x9e, 0x5f, 0x78, 0x92, 0x2e, 0x5f, 0x5, 0x3d, 0xd4, 0x77, 0xe6, 0x72, 0xd, 0xa7, 0x33, 0x50, 0x2a, 0xa3, 0x35, 0x9c, 0x5e, 0xd4, 0x1a, 0xcf, 0xfc, 0x19, 0x7c, 0xae, 0x35, 0x76, 0xbe, 0xa5, 0x5, 0x3a, 0x9f, 0xd5, 0x1a, 0xbb, 0x1e, 0xd4, 0x83, 0x3d, 0xb7, 0x1b, 0xe1, 0xbe, 0x4b, 0xcd, 0x48, 0xf2, 0x0, 0x4f, 0x2c, 0xe7, 0x9f, 0x4, 0x4b, 0x57, 0xc1, 0x13, 0x3f, 0x6b, 0x16, 0x8, 0xfc, 0xe9, 0x72, 0xd, 0x67, 0x94, 0xb4, 0xc6, 0x32, 0x67, 0x56, 0x41, 0x9e, 0xc, 0x2f, 0xeb, 0xfc, 0xa, 0xc6, 0x70, 0x97, 0xc7, 0xca, 0x2c, 0x3e, 0x8e, 0x53, 0x55, 0xc1, 0xb4, 0xb2, 0x1b, 0xeb, 0x70, 0xcd, 0x38, 0xb1, 0x29, 0x23, 0x92, 0x38, 0x79, 0xa4, 0xfd, 0xad, 0xbe, 0xff, 0x63, 0x53, 0x1b, 0xd1, 0xf4, 0x36, 0x7a, 0xa0, 0xf3, 0x77, 0x12, 0xb6, 0xe1, 0xc, 0x46, 0xb1, 0x94, 0x6, 0xac, 0x36, 0x26, 0x63, 0x28, 0xc2, 0x0, 0x1a, 0xd6, 0x3c, 0xf0, 0x89, 0xa9, 0x95, 0x32, 0x63, 0x18, 0x8c, 0x68, 0x66, 0x29, 0x23, 0x92, 0xdc, 0x5, 0xc2, 0x77, 0xe1, 0x7e, 0x7e, 0xaa, 0x2a, 0x4a, 0xa0, 0xf3, 0xf, 0xe4, 0xef, 0x30, 0x62, 0xf7, 0x1f, 0xec, 0x7d, 0x0, 0xfd, 0xfd, 0x83, 0xfe, 0x4a, 0x2, 0xea, 0xdf, 0x11, 0xee, 0x5, 0x7e, 0xc7, 0x55, 0x70, 0x4, 0xd2, 0xd3, 0x28, 0x95, 0xc6, 0xe6, 0xb4, 0xaf, 0x60, 0x46, 0x53, 0xbb, 0x18, 0xa1, 0xbe, 0xab, 0xb0, 0xd9, 0x3f, 0x83, 0xaa, 0x6b, 0xd6, 0x85, 0x8, 0xdf, 0x98, 0x56, 0x66, 0xbd, 0x61, 0x8f, 0x11, 0xcb, 0x2d, 0x36, 0x45, 0x9f, 0xc, 0x94, 0x6, 0x3, 0x1f, 0xfb, 0x2f, 0x23, 0xdc, 0x7f, 0x99, 0x1e, 0x18, 0x5d, 0x3, 0x50, 0xc2, 0x47, 0xb3, 0xcb, 0x43, 0xf8, 0x3d, 0xe1, 0xe6, 0xef, 0xc4, 0x26, 0x33, 0x50, 0x2, 0xd8, 0xf8, 0xae, 0x32, 0x8c, 0xc7, 0x94, 0xbf, 0x1c, 0x84, 0xc0, 0x9, 0x7e, 0xdc, 0x13, 0xcb, 0xcf, 0x3f, 0xf4, 0x97, 0x7f, 0x72, 0x7a, 0x33, 0x9a, 0xde, 0x1b, 0xfd, 0xd0, 0xe9, 0x47, 0x9f, 0x83, 0x86, 0xe6, 0x14, 0xe8, 0x2a, 0xe2, 0x1d, 0xf0, 0xa2, 0x27, 0x5e, 0x98, 0x56, 0xa9, 0x36, 0xa, 0x1, 0xaf, 0xea, 0xc5, 0x3d, 0xb1, 0x6c, 0xb, 0x4e, 0xfc, 0xdd, 0xd8, 0xdc, 0x12, 0x87, 0x8b, 0xce, 0xd3, 0x41, 0x21, 0x2, 0x44, 0x63, 0x4, 0xd2, 0x30, 0x98, 0x33, 0xe1, 0x9, 0x66, 0x1a, 0xd2, 0x78, 0x10, 0xe, 0x2f, 0xfa, 0x47, 0xf4, 0x60, 0x37, 0x84, 0xa4, 0xfe, 0x2a, 0xfd, 0xcb, 0x4, 0x44, 0x9a, 0x8b, 0xcf, 0xec, 0x34, 0xc6, 0xfe, 0xc1, 0xd7, 0x7a, 0xe1, 0x74, 0x4a, 0xbd, 0x51, 0x8, 0x10, 0x31, 0x3, 0x31, 0x4a, 0x53, 0xc0, 0x0, 0x17, 0x5e, 0xe4, 0xf3, 0x3c, 0x2a, 0xe3, 0x14, 0xc9, 0x8, 0xac, 0xac, 0x5, 0x61, 0x1c, 0xdd, 0xb1, 0xaf, 0xf5, 0xa2, 0xe9, 0x4c, 0x2b, 0x37, 0xbf, 0x27, 0x56, 0x58, 0x6, 0xc6, 0x66, 0x80, 0xd, 0xd1, 0xf6, 0x1f, 0xd1, 0x8f, 0xe8, 0xd3, 0x19, 0x31, 0x3e, 0x28, 0x95, 0xbd, 0x7, 0xfa, 0x55, 0x6, 0x30, 0x1a, 0x1, 0xae, 0xfc, 0xb, 0x3d, 0x20, 0x84, 0x1f, 0x4, 0x4c, 0x8, 0x82, 0x38, 0xd4, 0xfb, 0x11, 0xae, 0x82, 0xd5, 0x21, 0xec, 0xac, 0x38, 0xdd, 0xcb, 0x80, 0x55, 0xbc, 0xf1, 0xc2, 0x9a, 0xde, 0xa6, 0xc2, 0x5a, 0x65, 0xb1, 0xe1, 0x21, 0x3a, 0x60, 0x64, 0xdd, 0x78, 0x4c, 0x5e, 0x8b, 0xbb, 0xfb, 0x1, 0x78, 0x9a, 0x77, 0xe5, 0xbe, 0x6, 0xd, 0x79, 0x25, 0x1a, 0xf7, 0xaf, 0xd1, 0xb8, 0x2, 0x3c, 0x4d, 0xe7, 0xcc, 0xa2, 0x5e, 0xfb, 0xe1, 0xc4, 0xc3, 0xb8, 0x53, 0x8b, 0xa0, 0x4, 0x18, 0xc5, 0x32, 0x21, 0xe9, 0x3b, 0x0, 0xe1, 0x0, 0xd2, 0x24, 0x20, 0xee, 0xf5, 0xcb, 0x71, 0xaf, 0x1f, 0xd, 0x91, 0xef, 0x45, 0xde, 0xaf, 0xbc, 0x9e, 0x83, 0x90, 0x7c, 0x1c, 0xe0, 0x3c, 0x8e, 0x9c, 0xcf, 0xc7, 0x14, 0x75, 0xfe, 0x42, 0x5f, 0x23, 0x7a, 0x4, 0x7a, 0x9b, 0xce, 0x5a, 0x1a, 0xde, 0x6b, 0x6f, 0x18, 0xef, 0xf2, 0x75, 0xfe, 0x6, 0xc8, 0x9e, 0x82, 0x53, 0xf9, 0x43, 0x79, 0x73, 0x8d, 0x50, 0x6f, 0x15, 0x5d, 0x66, 0xa0, 0x7c, 0xb4, 0x41, 0x5c, 0xe4, 0x69, 0x81, 0x28, 0x23, 0xb1, 0x45, 0x1b, 0xa4, 0xed, 0x7c, 0xb9, 0x4c, 0x8c, 0xed, 0x38, 0x9e, 0x21, 0xc6, 0xf9, 0x1b, 0x1e, 0x60, 0x35, 0x33, 0x96, 0x5b, 0xc4, 0xdb, 0x7c, 0xee, 0xec, 0x43, 0x59, 0x2f, 0xea, 0xcf, 0xe2, 0x8d, 0x9f, 0x65, 0xea, 0xe1, 0xbe, 0x77, 0x78, 0xbf, 0x5f, 0x9a, 0xb1, 0xfc, 0x46, 0xde, 0xa6, 0xb3, 0x67, 0xa8, 0x4b, 0x3, 0xc0, 0xe2, 0xb7, 0x81, 0x1, 0x7c, 0x62, 0x88, 0xd, 0x66, 0x94, 0x1e, 0x1c, 0x8c, 0xb7, 0xb1, 0xd3, 0xf2, 0x77, 0x69, 0x94, 0xa9, 0xf1, 0x1e, 0xb8, 0x52, 0xde, 0xc1, 0x15, 0x73, 0x3a, 0xae, 0x1c, 0x1d, 0xcc, 0x8a, 0xbc, 0xff, 0xf4, 0xa, 0x38, 0xed, 0xb, 0xa2, 0xfe, 0xa1, 0x7a, 0x10, 0xfd, 0x10, 0x3d, 0xd5, 0xf9, 0x22, 0xff, 0x8, 0xfc, 0xef, 0x30, 0x5f, 0xfd, 0x79, 0x80, 0xa6, 0xb3, 0x97, 0xc1, 0x46, 0xbe, 0x5b, 0x39, 0x91, 0x10, 0x2d, 0xdc, 0x87, 0x8d, 0x96, 0x8, 0x3b, 0xe0, 0x58, 0x26, 0xb5, 0x1d, 0x29, 0x21, 0x82, 0xe6, 0x27, 0xf2, 0x78, 0x9a, 0x83, 0x77, 0xc6, 0x75, 0xf0, 0xa, 0x1b, 0x7a, 0x5b, 0xce, 0x9b, 0x7d, 0xa0, 0x5f, 0x9, 0x51, 0xa6, 0xe3, 0xaa, 0xba, 0x8f, 0xd7, 0x2f, 0xf2, 0xd8, 0xf6, 0x2c, 0xd4, 0x97, 0xc7, 0xca, 0xe4, 0x51, 0x6f, 0xa9, 0xfa, 0xf3, 0x2, 0x91, 0xe4, 0x6b, 0x7a, 0x79, 0x83, 0xab, 0x9b, 0xda, 0xcf, 0x41, 0xda, 0x19, 0x51, 0x67, 0xbc, 0x9, 0xf5, 0x31, 0xc2, 0x9e, 0x27, 0xd2, 0x95, 0xf9, 0x93, 0x21, 0x3c, 0xf, 0x11, 0x1b, 0xa4, 0xfb, 0x7e, 0x53, 0x94, 0x15, 0x1, 0xd5, 0x95, 0xfb, 0xe1, 0x6b, 0x29, 0x52, 0x1c, 0x49, 0x3d, 0x8c, 0xff, 0x29, 0xd6, 0xad, 0x2f, 0x3, 0x88, 0xa6, 0x1f, 0xe2, 0x1b, 0xc8, 0x45, 0x75, 0x3b, 0x10, 0x8d, 0x10, 0x79, 0x7c, 0xfe, 0x94, 0xaf, 0x93, 0xa0, 0xc9, 0x67, 0x60, 0x8, 0xdb, 0xe2, 0x6a, 0x68, 0xa5, 0xb2, 0x8a, 0xc8, 0x4c, 0xea, 0x8b, 0x11, 0xa2, 0xf, 0x6, 0x60, 0x4, 0x89, 0xf, 0xcc, 0x58, 0xa1, 0xa5, 0x8e, 0x1e, 0x82, 0xf9, 0x5d, 0xe1, 0x1e, 0x3f, 0xb5, 0x4f, 0x13, 0xc5, 0x13, 0x9, 0x31, 0x67, 0x56, 0xf5, 0x8, 0x38, 0xd1, 0xe0, 0x4f, 0x23, 0x9c, 0x0, 0x55, 0x8f, 0xc5, 0x78, 0x2c, 0x90, 0xda, 0x89, 0xba, 0x89, 0x6f, 0x60, 0x3c, 0x7, 0xd4, 0xcb, 0x3b, 0xc0, 0x84, 0x1, 0xbc, 0x42, 0x9b, 0x10, 0x49, 0x4c, 0x48, 0x20, 0x18, 0xab, 0xc4, 0xfd, 0x22, 0x3d, 0xe5, 0x37, 0x21, 0xca, 0x64, 0x50, 0xc6, 0xe4, 0x7e, 0xf4, 0x70, 0xe2, 0x73, 0x3c, 0x92, 0xf, 0x98, 0xec, 0xff, 0xcb, 0xc7, 0x70, 0xef, 0xbd, 0x86, 0x85, 0x17, 0xcb, 0x8b, 0x36, 0xb1, 0xf8, 0x9, 0xe, 0x93, 0xd3, 0xc3, 0xc0, 0xf6, 0x28, 0x48, 0x27, 0x3f, 0x81, 0x27, 0x38, 0x74, 0x92, 0x8a, 0x5f, 0xc8, 0xe0, 0xe4, 0x7f, 0x66, 0x9f, 0x2, 0x2c, 0x96, 0xf1, 0xb8, 0x9e, 0x21, 0x2f, 0x60, 0xef, 0x5, 0xe, 0xc7, 0x77, 0x78, 0x44, 0x9e, 0x30, 0x19, 0xff, 0xef, 0xbf, 0xdf, 0x8c, 0xa4, 0x7e, 0x31, 0xb0, 0x60, 0xc0, 0xc5, 0x47, 0x1c, 0x55, 0x88, 0xfd, 0xc0, 0x61, 0xb0, 0xb2, 0xf7, 0xfb, 0x5b, 0xcf, 0x77, 0xef, 0x8f, 0x44, 0xb0, 0xce, 0x6e, 0xb8, 0xf1, 0x13, 0xf0, 0xb, 0xd7, 0xdc, 0x83, 0xa9, 0xef, 0x6f, 0xbd, 0x60, 0x66, 0x2c, 0xea, 0xc1, 0xaa, 0xf0, 0xd1, 0x54, 0x19, 0x26, 0xd2, 0xa, 0xc0, 0xf7, 0x26, 0xfd, 0x15, 0x7e, 0x21, 0x3c, 0xd8, 0xb5, 0xe2, 0x43, 0xf4, 0xd5, 0x30, 0x49, 0xb8, 0xf0, 0xd4, 0xf, 0x10, 0xf5, 0x21, 0x3c, 0x58, 0x2e, 0x87, 0x31, 0x9c, 0x8c, 0xfc, 0xe5, 0x1c, 0xfe, 0xfc, 0x58, 0x16, 0xf5, 0x1e, 0xe6, 0x8b, 0x23, 0xd0, 0x96, 0x21, 0x6, 0x3, 0x6d, 0x44, 0x5a, 0xa4, 0x1d, 0xcb, 0x44, 0x1d, 0xc7, 0x6f, 0x30, 0xf8, 0xbe, 0xd3, 0x36, 0xe, 0x79, 0xce, 0xed, 0x6, 0x3f, 0x5f, 0xe7, 0x36, 0x6, 0x3f, 0x10, 0xa0, 0x7c, 0xfa, 0x6f, 0x5d, 0xbe, 0xfd, 0xa2, 0x99, 0xdd, 0x29, 0x7e, 0xcb, 0x85, 0xb3, 0xe3, 0xdf, 0xb6, 0x87, 0xb0, 0x80, 0x62, 0xed, 0x42, 0x33, 0x3f, 0x22, 0xff, 0x61, 0x18, 0x42, 0x2, 0x86, 0xd0, 0xe2, 0x6f, 0xbf, 0x68, 0xae, 0x4a, 0xfd, 0xf3, 0x56, 0xc2, 0x82, 0x9e, 0x94, 0x36, 0x85, 0x99, 0x56, 0xba, 0xc4, 0x51, 0x60, 0x3f, 0x80, 0x2d, 0xfe, 0xfb, 0xde, 0xe6, 0x73, 0xda, 0xdc, 0xfc, 0x7a, 0x3f, 0xdb, 0xb6, 0x60, 0x8f, 0x95, 0x61, 0x9c, 0xda, 0x93, 0x62, 0xe5, 0xde, 0x41, 0xbd, 0x2b, 0x40, 0x17, 0x8c, 0xe2, 0xbe, 0x4a, 0xdd, 0x34, 0xd5, 0x3, 0x76, 0x5b, 0x90, 0x16, 0x38, 0x7e, 0x67, 0xfe, 0x87, 0xb4, 0x8c, 0x63, 0x19, 0xcf, 0x77, 0x48, 0x3b, 0xd7, 0x1d, 0xfc, 0x5c, 0x9d, 0xe7, 0x3b, 0xe0, 0xba, 0xb8, 0xf8, 0xbc, 0xe, 0xa3, 0x7d, 0x89, 0x17, 0xce, 0x5d, 0x79, 0xa7, 0xab, 0xa7, 0x73, 0xa5, 0xf8, 0xbe, 0x96, 0xf3, 0x22, 0x34, 0x51, 0x12, 0x31, 0x23, 0x26, 0x2e, 0x16, 0x4e, 0x65, 0x44, 0x94, 0xc7, 0x94, 0x9f, 0x2d, 0xe2, 0xbb, 0xc6, 0x60, 0x14, 0x2, 0x3a, 0xfd, 0xd8, 0x1f, 0x6f, 0xbc, 0xf0, 0xa4, 0xaf, 0xe5, 0xfc, 0xd, 0x5c, 0x29, 0x3e, 0x1e, 0x71, 0x8b, 0xe3, 0x34, 0xbf, 0xc5, 0x5, 0x2d, 0x3, 0xb1, 0x21, 0x66, 0xc, 0x69, 0xe, 0xff, 0xa6, 0x32, 0x29, 0x5f, 0xe4, 0x59, 0x20, 0x36, 0x78, 0x44, 0x9b, 0x11, 0x62, 0x11, 0xa3, 0xd7, 0xc6, 0x12, 0xf1, 0xb0, 0xd6, 0x21, 0xd2, 0xd5, 0x83, 0x2, 0xd7, 0x7f, 0x90, 0x3b, 0x4f, 0x7e, 0xeb, 0x85, 0x33, 0x60, 0x72, 0xbd, 0x74, 0xaa, 0x69, 0xe2, 0x5c, 0x4c, 0x97, 0x62, 0xd6, 0x1a, 0x20, 0xe3, 0x79, 0x55, 0x44, 0x3d, 0xd7, 0xcc, 0x97, 0xf0, 0x36, 0xb9, 0xd0, 0x0, 0xfc, 0xed, 0x17, 0x4f, 0xb, 0xb7, 0xb4, 0x79, 0xed, 0x6, 0xe6, 0xdc, 0x2, 0x89, 0x2d, 0xa5, 0x1, 0xa5, 0x6b, 0x90, 0xda, 0x38, 0xaf, 0x43, 0xb4, 0xa7, 0xf4, 0x78, 0xad, 0x1, 0x94, 0xef, 0xff, 0x47, 0x7c, 0xad, 0x17, 0xac, 0xef, 0x2a, 0x3, 0x58, 0xbe, 0xfd, 0xe2, 0x85, 0xf1, 0x98, 0x7b, 0xca, 0x63, 0xfd, 0xcb, 0xbd, 0x39, 0x0, 0x3b, 0x8f, 0x45, 0x71, 0xbc, 0x9f, 0x6d, 0xd, 0xd6, 0xae, 0xd7, 0xb6, 0xed, 0xdd, 0x87, 0x6a, 0x6d, 0xdb, 0xb6, 0x8d, 0xc7, 0xba, 0xfd, 0xb4, 0xb6, 0x6d, 0xdb, 0xb6, 0xed, 0xde, 0xee, 0x2f, 0xed, 0x49, 0x5e, 0xa6, 0x93, 0x4f, 0x2f, 0x49, 0x1f, 0x32, 0xf3, 0x9f, 0x73, 0x73, 0x93, 0x1e, 0xfe, 0xef, 0xe9, 0x4d, 0xa6, 0xd5, 0x93, 0x73, 0x9d, 0xcd, 0xa4, 0x5c, 0x37, 0xd7, 0x6b, 0x60, 0x7e, 0xef, 0x37, 0xfc, 0xc0, 0x37, 0x5, 0x6a, 0x3f, 0xf7, 0x23, 0x9, 0xfd, 0x0, 0xbc, 0x2, 0x9e, 0xe6, 0xbd, 0xc5, 0xc3, 0xbe, 0xc6, 0xd6, 0x7, 0x79, 0xbe, 0xbe, 0x9f, 0x78, 0x90, 0x2d, 0x8f, 0x82, 0x67, 0x19, 0xbf, 0xce, 0xbd, 0x5f, 0xf0, 0x99, 0xff, 0x2a, 0x3a, 0x80, 0xb5, 0xd, 0xfb, 0x64, 0xb0, 0x88, 0x4f, 0xf4, 0x32, 0xb7, 0xeb, 0x75, 0x25, 0xed, 0x1c, 0x9f, 0xee, 0xd, 0x44, 0x52, 0xbd, 0xe3, 0xb7, 0x1, 0xe1, 0xfd, 0x1f, 0x1a, 0x0, 0x23, 0xb7, 0x16, 0x67, 0x4b, 0x38, 0x59, 0xd6, 0xe1, 0xab, 0x82, 0x79, 0xa4, 0x35, 0x4c, 0xd7, 0xe4, 0x73, 0x48, 0x81, 0xa1, 0xc3, 0x7c, 0xaf, 0xa1, 0x5f, 0xe6, 0x2c, 0xee, 0x17, 0x2, 0x32, 0xd6, 0x24, 0x30, 0x25, 0xf4, 0x6f, 0x92, 0xf7, 0x31, 0x5, 0x7d, 0x96, 0x56, 0x7a, 0x23, 0x8f, 0x9f, 0x67, 0xfa, 0x9a, 0x3a, 0x23, 0xf8, 0xbf, 0x26, 0x5f, 0x61, 0x4b, 0xfb, 0x23, 0xe9, 0x89, 0x24, 0x76, 0x94, 0xb7, 0x29, 0x39, 0x34, 0x14, 0xcf, 0x8f, 0x61, 0x3c, 0xd, 0x78, 0xb9, 0x67, 0x43, 0x5f, 0x63, 0xc7, 0xc1, 0xbc, 0xab, 0x48, 0x43, 0x8e, 0x7, 0xf9, 0xfc, 0xab, 0x10, 0xe6, 0xfb, 0x5a, 0xb2, 0x55, 0xfc, 0xec, 0xf2, 0x4f, 0x89, 0xaf, 0x96, 0xf1, 0xc8, 0x9c, 0x39, 0x6e, 0xa4, 0xc5, 0xbc, 0x71, 0x4d, 0xf4, 0x49, 0x9e, 0x79, 0x8c, 0xbe, 0x7a, 0xc9, 0xed, 0xae, 0x18, 0xd8, 0xf3, 0xed, 0x3f, 0x92, 0xe, 0x91, 0xd4, 0xd7, 0x2a, 0x4e, 0xeb, 0x81, 0x3, 0x76, 0xab, 0x95, 0x73, 0x43, 0x2, 0x39, 0x9f, 0xcb, 0x9c, 0x71, 0xcd, 0xfa, 0x7c, 0xee, 0x73, 0xaa, 0x66, 0x5e, 0x19, 0xfa, 0x1b, 0xae, 0xff, 0xe, 0x1f, 0x5f, 0xa2, 0x80, 0x19, 0x8a, 0xdd, 0x10, 0x8e, 0xe7, 0x47, 0xd8, 0xdb, 0xef, 0xa4, 0x97, 0xa3, 0x0, 0xa7, 0xb0, 0xe7, 0xb9, 0x1b, 0xbd, 0xef, 0x51, 0x98, 0xbf, 0x88, 0xd9, 0xec, 0x7, 0xe8, 0xca, 0x47, 0x4d, 0xcc, 0xf3, 0x8c, 0x4b, 0xe6, 0x6b, 0xa5, 0xe8, 0x12, 0x42, 0x37, 0xb4, 0x7c, 0x42, 0x2c, 0xbb, 0xf7, 0x6, 0x2, 0x6c, 0xc2, 0x6a, 0x28, 0xf1, 0xa6, 0xcf, 0x8, 0xb6, 0xce, 0x30, 0xdb, 0x55, 0x66, 0x1f, 0x58, 0xa9, 0x3f, 0xb0, 0x62, 0xdf, 0x64, 0x7f, 0x72, 0x4, 0x6f, 0xcd, 0x86, 0xbb, 0xb4, 0xf9, 0x5d, 0x92, 0x47, 0xdf, 0x2c, 0x64, 0x78, 0x9b, 0x3c, 0xfc, 0x29, 0x44, 0x30, 0x16, 0x82, 0xb, 0xb1, 0x1a, 0x64, 0xa7, 0x8b, 0x3d, 0xe2, 0x8f, 0xe4, 0x86, 0xf5, 0x2c, 0x1, 0x62, 0x85, 0x61, 0xbe, 0xa6, 0x64, 0x33, 0xc9, 0xfe, 0xcf, 0x5b, 0x71, 0xaa, 0x45, 0x69, 0x90, 0xb1, 0xeb, 0xc0, 0x8e, 0xd2, 0x65, 0xd7, 0xb8, 0xe5, 0xf, 0x7e, 0x5d, 0xfb, 0x1d, 0xed, 0x7b, 0xff, 0xba, 0x3d, 0x5, 0xc5, 0x8a, 0x83, 0xe9, 0xa, 0x1d, 0xd8, 0xfe, 0x52, 0xf3, 0x43, 0x5b, 0x10, 0xc8, 0x5a, 0x3f, 0x9d, 0x8e, 0xfd, 0x3f, 0x6c, 0xe6, 0x7a, 0x7e, 0x13, 0x18, 0xcb, 0x8d, 0x60, 0x25, 0x1c, 0x42, 0xf0, 0xa, 0x48, 0xe0, 0xad, 0xba, 0xa3, 0xf3, 0x8, 0xbc, 0xd5, 0x12, 0xe6, 0x7b, 0xac, 0xe7, 0xc, 0x3b, 0xa, 0x30, 0xd6, 0x93, 0xdc, 0xf6, 0x33, 0xdf, 0xd9, 0x97, 0xfa, 0x23, 0x99, 0xa1, 0x3d, 0xb3, 0x20, 0xf2, 0x23, 0xf9, 0x11, 0xcb, 0xeb, 0xf8, 0xf2, 0x87, 0x9e, 0xb, 0xa4, 0x4e, 0xd2, 0xda, 0x58, 0xe6, 0x12, 0xab, 0xc0, 0xf2, 0x1e, 0x20, 0x8b, 0xc, 0x5b, 0x2f, 0xd1, 0x85, 0xa7, 0x59, 0xfd, 0xcd, 0x3d, 0x10, 0xcd, 0xe, 0xa, 0x26, 0x66, 0xc, 0xf6, 0x37, 0x77, 0xe, 0x61, 0x3c, 0x34, 0x10, 0xcd, 0xd, 0x72, 0x91, 0x4, 0x79, 0x48, 0x90, 0x3c, 0x87, 0x60, 0x4b, 0xac, 0xbe, 0x4a, 0x61, 0x4, 0x65, 0xce, 0x2b, 0xd2, 0x29, 0x78, 0xbb, 0xf4, 0x29, 0xfd, 0x5c, 0x83, 0xbf, 0xb1, 0xed, 0x3b, 0x1e, 0x49, 0xd7, 0xec, 0x25, 0xef, 0x45, 0x8e, 0xc6, 0xa7, 0xef, 0x29, 0x52, 0x6d, 0xfc, 0x4e, 0xe4, 0xc2, 0x4c, 0xfa, 0x32, 0x5d, 0xe0, 0xd1, 0x4a, 0xd, 0xe2, 0xc5, 0x61, 0xd8, 0x1d, 0x1e, 0x8c, 0x15, 0xc6, 0x53, 0xf0, 0x15, 0x2, 0x91, 0xcc, 0x26, 0x6c, 0x6c, 0x77, 0x86, 0x20, 0xbb, 0x33, 0x7f, 0x12, 0x73, 0x1b, 0xd0, 0x15, 0x87, 0xb8, 0x16, 0x34, 0xc, 0x1b, 0x8a, 0xa1, 0x33, 0x70, 0x8e, 0xd6, 0xd4, 0x66, 0x10, 0x80, 0xb1, 0xf3, 0x40, 0x37, 0x52, 0x27, 0xc4, 0xbf, 0x7c, 0xf, 0x3f, 0x18, 0x4a, 0x14, 0xc7, 0x7b, 0x7a, 0xd1, 0xa1, 0x25, 0x1c, 0x1f, 0x3f, 0xd1, 0xa, 0x25, 0x79, 0x50, 0x86, 0xef, 0xf6, 0xe3, 0x37, 0x24, 0x5d, 0xe0, 0x4f, 0x48, 0xf0, 0x16, 0xdd, 0xef, 0x27, 0xce, 0x4d, 0xd7, 0x6a, 0xc7, 0x6d, 0xcf, 0x43, 0x8a, 0xd, 0x5d, 0xd, 0x3a, 0xb4, 0xfb, 0xac, 0xc1, 0x30, 0xee, 0x70, 0x56, 0xe3, 0x3f, 0x3a, 0x5b, 0x19, 0x97, 0x9d, 0x86, 0x4e, 0x2e, 0xc6, 0x14, 0xbf, 0xf3, 0x26, 0x4f, 0x2f, 0x3d, 0xe8, 0x48, 0xcb, 0xf8, 0x1a, 0xda, 0x5f, 0x17, 0x5f, 0x55, 0xd5, 0x77, 0xa4, 0xfd, 0xf8, 0xd, 0x9, 0x94, 0x9, 0xc6, 0x35, 0x8b, 0x71, 0x89, 0x8e, 0xb0, 0x8f, 0xfb, 0xef, 0x6, 0x76, 0x9f, 0x3d, 0x38, 0x18, 0xcd, 0x36, 0xf2, 0xd8, 0xc5, 0x2e, 0x5c, 0x73, 0xb8, 0x7d, 0xde, 0x30, 0x7, 0x38, 0x97, 0x39, 0xa0, 0x27, 0x11, 0xe6, 0x13, 0x5c, 0x73, 0xf2, 0x4e, 0x4f, 0x2f, 0x3f, 0x2, 0xb1, 0xdc, 0x12, 0x74, 0x28, 0xde, 0x3d, 0x48, 0xf1, 0xcd, 0xf1, 0x21, 0xad, 0xcf, 0xe7, 0x7d, 0x8f, 0xe8, 0x2, 0xe6, 0xeb, 0xd5, 0x39, 0x73, 0xde, 0xb5, 0xb1, 0x7e, 0xce, 0xe2, 0x3c, 0x67, 0x89, 0xe8, 0xad, 0x3, 0x5c, 0xf, 0x3a, 0xb4, 0xdb, 0x8c, 0x61, 0xb4, 0xa5, 0xcf, 0xb4, 0xf6, 0xa3, 0x3b, 0x69, 0x17, 0xe8, 0xd2, 0xf5, 0x28, 0xc6, 0x5a, 0xf1, 0xef, 0x5d, 0x3a, 0x71, 0x87, 0xb, 0xc1, 0xb8, 0xb2, 0x51, 0xf6, 0xd2, 0xa9, 0x1e, 0xd1, 0xf3, 0x21, 0xb2, 0x5e, 0x50, 0xba, 0x4d, 0x5e, 0x7e, 0x7d, 0x10, 0x8c, 0x65, 0x1b, 0xea, 0xd0, 0xfa, 0x72, 0x61, 0x8, 0xf0, 0x35, 0x28, 0xc3, 0x7e, 0xc7, 0x80, 0xbe, 0x12, 0x80, 0xc9, 0xa9, 0x3b, 0xf8, 0xce, 0x1f, 0xe7, 0xe9, 0x43, 0x7, 0x4f, 0x26, 0xd, 0x90, 0xe0, 0x1d, 0xc9, 0x89, 0x42, 0x2a, 0x89, 0xab, 0x2e, 0xc0, 0x5e, 0x5, 0xec, 0x3, 0xdc, 0xff, 0x21, 0x29, 0xef, 0x6, 0x1e, 0xe4, 0xf5, 0xe9, 0xbf, 0xe, 0x13, 0x80, 0xa4, 0x21, 0x9b, 0x93, 0xaf, 0xf0, 0xd4, 0xb1, 0xb9, 0xa7, 0xf, 0x1e, 0x90, 0xe0, 0x78, 0x56, 0xe1, 0xaf, 0x5a, 0x1c, 0x75, 0x2e, 0xbe, 0xe4, 0xf, 0x19, 0x49, 0x5f, 0xec, 0x6e, 0xfb, 0x8f, 0x17, 0xc6, 0x12, 0xe4, 0x4f, 0x55, 0xe3, 0x9d, 0x40, 0xa4, 0x2d, 0x18, 0x3a, 0x7e, 0x66, 0x77, 0x7d, 0x8c, 0xa7, 0x8f, 0x1e, 0x10, 0x77, 0x2c, 0x5, 0x68, 0x97, 0x78, 0x94, 0xc4, 0x26, 0xd2, 0x45, 0x60, 0x43, 0x0, 0x1, 0x52, 0xf7, 0xb2, 0x2f, 0x59, 0xde, 0x95, 0x0, 0xb5, 0x7f, 0xbf, 0xd2, 0x9a, 0x37, 0x80, 0x0, 0xbf, 0x6b, 0xc6, 0x2, 0xcd, 0x4, 0xd9, 0xdc, 0x89, 0xec, 0x3e, 0x8, 0xa0, 0xaa, 0x7, 0xc9, 0xae, 0x3a, 0xcd, 0xcb, 0x96, 0x29, 0x9e, 0x3e, 0x7c, 0x4, 0xe3, 0x85, 0xb5, 0x28, 0xc2, 0xe3, 0x46, 0x61, 0x9a, 0x91, 0xb, 0x9a, 0x13, 0x8b, 0x31, 0xd2, 0x72, 0xde, 0x9c, 0x47, 0x91, 0x7f, 0xb3, 0x88, 0x4e, 0x77, 0x25, 0x38, 0x94, 0xf, 0x44, 0xf9, 0x75, 0xb4, 0xe9, 0xbf, 0xab, 0x6, 0x93, 0x8e, 0x0, 0x5d, 0x4, 0x94, 0xfa, 0x90, 0xbd, 0xc5, 0xe, 0x9e, 0x7e, 0x70, 0xb0, 0x2, 0x8f, 0xaf, 0xc6, 0x94, 0x2c, 0x49, 0x8c, 0x75, 0x1, 0x36, 0x95, 0x26, 0xd9, 0x8, 0xa6, 0x5d, 0xfc, 0x5d, 0x60, 0xf2, 0xb, 0xd8, 0x87, 0xa1, 0x94, 0x16, 0x60, 0x19, 0xd9, 0x3d, 0x44, 0xb4, 0xcf, 0x57, 0x1c, 0x56, 0x9a, 0x1e, 0x88, 0x75, 0x1, 0xab, 0x67, 0x54, 0x7f, 0x20, 0x0, 0x71, 0x4, 0xd9, 0x8c, 0xdd, 0x6b, 0xe4, 0x87, 0x18, 0xe7, 0x92, 0x7, 0xa4, 0x13, 0x90, 0x7c, 0xa2, 0xf, 0x2, 0x4c, 0x5f, 0x7a, 0xe7, 0x96, 0x81, 0xce, 0x7, 0x96, 0x98, 0xbe, 0xb8, 0x5f, 0xa, 0x86, 0xa1, 0x8a, 0x31, 0xbb, 0xa8, 0xea, 0x4a, 0x7f, 0x45, 0xd2, 0x8c, 0x8d, 0x5f, 0x3f, 0x21, 0x41, 0xdc, 0x66, 0x9e, 0xba, 0x91, 0x47, 0x91, 0xd1, 0xcc, 0x5d, 0x81, 0x78, 0x61, 0x61, 0x67, 0x5f, 0x0, 0xed, 0x71, 0xe3, 0xe0, 0x40, 0xac, 0x10, 0xa3, 0x55, 0xff, 0x7, 0x24, 0xb0, 0x74, 0xb9, 0xbb, 0x40, 0x87, 0x21, 0x79, 0x8e, 0xbe, 0x90, 0x77, 0xb, 0x53, 0xfa, 0x13, 0x1, 0x58, 0x2c, 0xeb, 0x10, 0xdf, 0xb7, 0xa0, 0xec, 0x26, 0xc8, 0x9f, 0xd4, 0x21, 0xa5, 0x18, 0x4b, 0x5e, 0x33, 0xaf, 0x53, 0xab, 0xd, 0xdd, 0xd8, 0xe5, 0x5e, 0xd, 0xc3, 0x84, 0x0, 0x69, 0xfb, 0x68, 0xc6, 0x69, 0x58, 0x1b, 0x4a, 0x4c, 0xdf, 0xd5, 0xd3, 0xcf, 0x8e, 0xf0, 0xee, 0x33, 0x27, 0xb2, 0x61, 0x3e, 0x5d, 0x62, 0x55, 0x5a, 0xac, 0xee, 0x14, 0x5e, 0x8a, 0x8f, 0xd4, 0xc0, 0xea, 0x7f, 0x9f, 0xee, 0x73, 0x56, 0x30, 0x3e, 0x7d, 0x39, 0x9b, 0xc, 0x2e, 0xe, 0xf, 0xed, 0x36, 0x73, 0x51, 0x14, 0xad, 0x1f, 0x88, 0x17, 0x23, 0xc1, 0x78, 0xf1, 0x40, 0x56, 0xea, 0x1b, 0x18, 0x2c, 0x1, 0xcd, 0x90, 0x6d, 0x88, 0x9e, 0x4f, 0x43, 0x89, 0x19, 0x6b, 0x7a, 0xfa, 0xe1, 0x11, 0xde, 0x6d, 0xe6, 0xca, 0x5a, 0x8c, 0x8e, 0xe6, 0xcb, 0xac, 0x8b, 0xc2, 0x9b, 0xe6, 0xbe, 0xd, 0xc6, 0xf3, 0xb3, 0x58, 0x4c, 0xdb, 0xfb, 0xe2, 0x33, 0x7, 0x77, 0xa3, 0xe0, 0x33, 0xc6, 0x51, 0xec, 0x55, 0x29, 0xf4, 0xe, 0xb4, 0xaf, 0x4, 0xf2, 0x68, 0x98, 0xd4, 0xc9, 0xe6, 0xec, 0x9d, 0x40, 0x4, 0x63, 0x20, 0x18, 0x35, 0xa0, 0x19, 0x56, 0xda, 0xd8, 0x6, 0xaa, 0xce, 0xc7, 0xa, 0x47, 0x62, 0x7b, 0x74, 0x7f, 0x24, 0x0, 0xb, 0x68, 0x45, 0xf2, 0xf7, 0x83, 0xe4, 0xce, 0x9, 0x28, 0x73, 0xee, 0x91, 0xac, 0xfa, 0xcc, 0xf7, 0xd8, 0xb8, 0x8f, 0x7a, 0x1d, 0x1a, 0xde, 0x7d, 0xd6, 0xc4, 0xee, 0x32, 0x75, 0x1a, 0x2b, 0xfe, 0x30, 0x1e, 0xc3, 0xde, 0x92, 0x62, 0xeb, 0xc0, 0x68, 0x56, 0x87, 0x32, 0xcf, 0xd9, 0x84, 0x2, 0x15, 0x5d, 0x7c, 0xf7, 0xaf, 0xe6, 0xe9, 0xa7, 0x7, 0xc4, 0x5e, 0x96, 0xe2, 0x3c, 0x23, 0x39, 0x53, 0x8e, 0xe4, 0x4d, 0xea, 0x80, 0xde, 0x2f, 0x59, 0xf1, 0x4f, 0x51, 0xf8, 0x13, 0x58, 0xb0, 0xa3, 0x6c, 0xb6, 0xaa, 0x19, 0xff, 0x73, 0x77, 0xce, 0x51, 0x96, 0x2b, 0x69, 0x0, 0x3f, 0xe7, 0xd9, 0xb6, 0xed, 0x77, 0xb5, 0xb6, 0x6d, 0x7b, 0xdb, 0x5a, 0xdb, 0xb6, 0x6d, 0x7b, 0x9b, 0xd7, 0xcd, 0x35, 0x9f, 0xcd, 0xb1, 0x6d, 0xab, 0xbb, 0xb2, 0xbf, 0x4a, 0xbe, 0x4c, 0x32, 0x75, 0x36, 0xe7, 0x4e, 0xcf, 0xad, 0x34, 0xf2, 0xc7, 0xef, 0x54, 0xa5, 0x92, 0xfb, 0xb9, 0xaa, 0x82, 0xc1, 0x5b, 0x49, 0xfe, 0x3a, 0x84, 0x22, 0x98, 0xa, 0x3, 0x1e, 0x25, 0x1c, 0x4d, 0xa8, 0x2f, 0xfc, 0x1e, 0x22, 0x8e, 0x27, 0x89, 0xd6, 0xc7, 0x2c, 0x79, 0x6c, 0x52, 0xb, 0x0, 0xdf, 0x4e, 0x25, 0x41, 0x9f, 0xc2, 0xcf, 0x88, 0x18, 0xfc, 0x5e, 0x30, 0x8e, 0x5, 0xf3, 0x5c, 0x38, 0x6e, 0x24, 0xfd, 0xbd, 0xd7, 0xbd, 0xb1, 0xfb, 0x70, 0x5b, 0x5f, 0xf7, 0x3e, 0x4d, 0x1, 0xf0, 0x1a, 0x36, 0x30, 0x82, 0xbe, 0x92, 0x7e, 0x6c, 0xf0, 0xc2, 0x64, 0xd7, 0x8d, 0x8d, 0x7d, 0x8f, 0x12, 0x33, 0x92, 0x5a, 0x4, 0x4f, 0x94, 0xb8, 0x5a, 0x8b, 0x27, 0xb9, 0xda, 0x74, 0x63, 0x43, 0xdf, 0xa9, 0x16, 0x97, 0xaa, 0xee, 0x9f, 0x22, 0x14, 0xe1, 0x7f, 0x70, 0xa4, 0xd, 0x61, 0x1c, 0xd7, 0x42, 0x8c, 0xac, 0x71, 0x5e, 0xc9, 0xf1, 0xd2, 0x54, 0x63, 0x7f, 0x3a, 0xc9, 0x5, 0xc0, 0xbe, 0x7c, 0x6d, 0x10, 0x57, 0x30, 0xe2, 0x74, 0x70, 0xf1, 0xa2, 0x15, 0x19, 0x1a, 0x96, 0xfe, 0xed, 0x99, 0xe6, 0xfc, 0xb9, 0x16, 0x5f, 0x5a, 0x74, 0xf7, 0x6a, 0x45, 0x8, 0xd6, 0x28, 0x69, 0xa7, 0x80, 0x3f, 0xde, 0x94, 0x6a, 0xea, 0xbf, 0x3a, 0xd1, 0x5, 0xd0, 0x9c, 0xbf, 0x6, 0x5f, 0x27, 0x2c, 0xc7, 0x6e, 0x77, 0xba, 0x69, 0xe0, 0x3c, 0x8b, 0xcb, 0x54, 0x6f, 0x51, 0xaa, 0x4c, 0xf1, 0xb5, 0xcf, 0x89, 0x1b, 0xf4, 0x78, 0x6d, 0x43, 0xcf, 0x40, 0xaa, 0x69, 0xe0, 0xe2, 0x24, 0x17, 0x0, 0x33, 0xf5, 0x4a, 0x7c, 0x5d, 0x2f, 0x3e, 0xdb, 0x62, 0x3c, 0xdd, 0x3c, 0xad, 0x5, 0xd0, 0x1d, 0x3d, 0x2e, 0x44, 0x9f, 0xb, 0x40, 0xef, 0xf7, 0xd8, 0x2, 0xce, 0x4e, 0xf8, 0xa, 0x70, 0x19, 0x2b, 0xec, 0x82, 0x20, 0x6, 0xd2, 0x46, 0xc7, 0x25, 0x32, 0xd6, 0x37, 0xa, 0x29, 0xa0, 0xb0, 0xce, 0xb7, 0x79, 0xa3, 0x52, 0x14, 0xe1, 0xea, 0x0, 0x43, 0x1a, 0xa4, 0x8d, 0xa2, 0x21, 0x7a, 0xac, 0xd6, 0xef, 0x5d, 0x7d, 0xd, 0xc9, 0x2f, 0x80, 0x54, 0x93, 0x14, 0x0, 0x3e, 0xd7, 0x4b, 0x50, 0x0, 0xdd, 0x14, 0x40, 0xc1, 0x5e, 0x1, 0xa4, 0x75, 0x1, 0x78, 0xa, 0x94, 0x4e, 0x9a, 0x1, 0xf4, 0x84, 0xe8, 0x3e, 0x34, 0x8c, 0xdf, 0x6a, 0x7d, 0x14, 0x40, 0x9e, 0x0, 0x5d, 0x9c, 0xec, 0x15, 0xa0, 0x70, 0x25, 0xbe, 0x6e, 0xa8, 0x15, 0x1b, 0xa8, 0xd1, 0x7, 0xe9, 0x23, 0x6f, 0x3c, 0xd5, 0x68, 0x75, 0xb, 0xe8, 0xeb, 0xe5, 0x8d, 0x1c, 0x82, 0x5d, 0x25, 0xa, 0x9c, 0x98, 0x11, 0x5d, 0xbd, 0x37, 0x11, 0xa0, 0x44, 0xdf, 0x4, 0x66, 0x5a, 0x8a, 0xd7, 0xe8, 0x98, 0xe2, 0xaf, 0xcd, 0xd8, 0xed, 0x4e, 0x59, 0xbd, 0x9, 0x6c, 0xea, 0xff, 0xe9, 0x8d, 0x5a, 0x30, 0xb0, 0x2f, 0x7, 0xec, 0x3f, 0xa6, 0x5, 0x12, 0x6, 0x72, 0x2c, 0xd7, 0x4, 0x63, 0x26, 0xe6, 0xb5, 0xe1, 0xeb, 0x9, 0x8, 0xb2, 0xe9, 0x2f, 0xa5, 0x0, 0x52, 0xc9, 0x2e, 0x80, 0xc2, 0xb5, 0x7e, 0xc, 0x25, 0x1e, 0x26, 0x46, 0xbc, 0x8c, 0x6b, 0x18, 0xf3, 0x5a, 0x81, 0x3e, 0xb1, 0xdb, 0x4e, 0xdc, 0xce, 0xb5, 0xf8, 0xac, 0x9a, 0xff, 0x34, 0x8a, 0xb6, 0x84, 0x92, 0x1e, 0x37, 0x52, 0xc9, 0x3d, 0xbb, 0x52, 0xcd, 0x85, 0x47, 0x25, 0x7c, 0x5, 0x78, 0x8a, 0xf8, 0xab, 0xec, 0xc5, 0xae, 0x77, 0x13, 0x5, 0x70, 0xb2, 0xc5, 0x15, 0x60, 0xe0, 0x2d, 0x6c, 0x3, 0xeb, 0x74, 0xb5, 0x71, 0x3f, 0x0, 0xbd, 0xca, 0x6d, 0x41, 0xc6, 0xac, 0xe3, 0xaf, 0x1c, 0x38, 0xf2, 0xd8, 0xc4, 0xee, 0xff, 0x2d, 0xc5, 0xd3, 0x88, 0xed, 0x67, 0x6d, 0xc7, 0x90, 0x89, 0x43, 0xdc, 0xf2, 0x5f, 0xe6, 0x25, 0xd3, 0x61, 0x76, 0xaa, 0xb4, 0xb5, 0x74, 0x1, 0x37, 0x63, 0x9d, 0xbc, 0x5c, 0xf8, 0x7b, 0xa, 0x5, 0x1, 0x18, 0x2e, 0x7d, 0xbe, 0x6f, 0xab, 0xb4, 0xd7, 0x42, 0xbf, 0x10, 0x36, 0xac, 0x5f, 0xda, 0x10, 0xe6, 0xf5, 0x72, 0xd, 0x72, 0x15, 0xc7, 0x9e, 0xdc, 0xe6, 0x62, 0x52, 0xb, 0x80, 0xb8, 0x96, 0xaf, 0x61, 0x7b, 0xbd, 0xfd, 0x46, 0xb7, 0x0, 0x24, 0x5e, 0x12, 0xb3, 0x50, 0x1f, 0xcc, 0x78, 0x1a, 0xc8, 0x78, 0x28, 0x7e, 0xd0, 0xb7, 0x89, 0x7c, 0xfd, 0x83, 0x42, 0xf8, 0x6c, 0xba, 0xa5, 0x74, 0x8a, 0x8d, 0x6a, 0x3d, 0x1e, 0x41, 0x37, 0x30, 0x23, 0x9f, 0x9b, 0x6e, 0x29, 0x34, 0xd2, 0x7e, 0x12, 0xe1, 0xbf, 0xc4, 0x81, 0x3b, 0x30, 0x6e, 0xf, 0x38, 0x3e, 0x28, 0x57, 0xb4, 0x1a, 0xa7, 0xe, 0x94, 0x57, 0x0, 0x85, 0xf, 0x64, 0xdb, 0xaa, 0x27, 0x26, 0xb3, 0x0, 0x4a, 0x39, 0x9d, 0x28, 0x10, 0x9f, 0xeb, 0x23, 0x15, 0x9e, 0x8c, 0xc1, 0xd8, 0x16, 0xa, 0xe1, 0x6f, 0x4c, 0xe0, 0xf7, 0x90, 0xc3, 0x33, 0xac, 0x19, 0x9f, 0x6b, 0x1f, 0x3c, 0x91, 0xa, 0xbe, 0x98, 0x19, 0xfa, 0x38, 0xda, 0x97, 0xd2, 0xb6, 0xa3, 0xe8, 0x7e, 0x14, 0x4e, 0xa0, 0xd8, 0x9a, 0x43, 0x14, 0xd8, 0xca, 0x6c, 0x6b, 0xf9, 0x9, 0x9, 0xdd, 0x2, 0x1e, 0xe5, 0x27, 0xdf, 0x52, 0x11, 0x28, 0x21, 0x98, 0x88, 0x22, 0x9b, 0x8f, 0x6a, 0x1b, 0xd8, 0x6e, 0xca, 0xe8, 0x7c, 0x4d, 0xae, 0xad, 0x7c, 0x64, 0x5c, 0xe, 0x7d, 0x87, 0x22, 0x18, 0x7, 0x9d, 0x38, 0x87, 0xb6, 0x2e, 0x90, 0xa1, 0xc0, 0x61, 0xe5, 0x79, 0x4d, 0xd2, 0x92, 0xcf, 0xaa, 0x76, 0x3a, 0xf1, 0xfa, 0x9c, 0xc4, 0x49, 0x4d, 0x3e, 0x3e, 0x35, 0xe3, 0xab, 0xcc, 0x38, 0x8a, 0xae, 0x25, 0xe8, 0xfd, 0x52, 0xba, 0xa5, 0x7c, 0x83, 0x55, 0x87, 0x72, 0x9d, 0x23, 0x47, 0x30, 0x53, 0xdf, 0x80, 0x61, 0xe3, 0xae, 0xa2, 0xe6, 0x1, 0x5, 0xce, 0xc1, 0x93, 0x17, 0xa4, 0xef, 0xc9, 0x70, 0xc1, 0xe0, 0x6f, 0x66, 0xdb, 0xca, 0x67, 0x27, 0xab, 0x0, 0x2a, 0x4f, 0x26, 0x19, 0xeb, 0x3, 0x3f, 0xf3, 0x66, 0x1c, 0x26, 0x43, 0xf4, 0x6f, 0x49, 0xbe, 0xf4, 0x69, 0xc1, 0xd5, 0x97, 0x7f, 0x88, 0xed, 0xe7, 0x59, 0x71, 0xdc, 0xd4, 0x5c, 0xe2, 0x57, 0x1e, 0xef, 0xa2, 0x15, 0x38, 0x36, 0xc0, 0xe0, 0xb5, 0xc8, 0x7e, 0x41, 0xc2, 0xf6, 0xff, 0x26, 0x99, 0xa1, 0x76, 0xe2, 0x24, 0xa4, 0xa3, 0xcf, 0x29, 0xf0, 0x56, 0x84, 0xe6, 0xfc, 0x70, 0xb6, 0xbd, 0x7a, 0x41, 0x5c, 0x2f, 0x36, 0x56, 0x84, 0x95, 0xd1, 0x1a, 0x14, 0x4, 0xfa, 0x46, 0xb, 0xe6, 0xb8, 0xd2, 0x20, 0x7, 0xc7, 0x8a, 0xdf, 0xc8, 0xb4, 0x55, 0x4f, 0x4e, 0xc4, 0xde, 0xdf, 0x5a, 0x4e, 0xe3, 0xcf, 0x5f, 0xd3, 0x4d, 0x92, 0x34, 0x29, 0x82, 0x38, 0x11, 0x5d, 0x12, 0xcb, 0x42, 0x8f, 0xfc, 0x27, 0x96, 0xd6, 0xbf, 0x6c, 0x1d, 0x96, 0x69, 0x29, 0xfd, 0x80, 0x76, 0xf, 0x38, 0x14, 0x83, 0x15, 0x90, 0x5, 0x85, 0xa5, 0xec, 0x5b, 0xaf, 0x4c, 0xc8, 0xe3, 0xdf, 0x47, 0x24, 0x3e, 0x4a, 0xda, 0x29, 0x1, 0x5d, 0xa2, 0xaf, 0xf4, 0xeb, 0x58, 0x1c, 0xd3, 0xff, 0x3a, 0x55, 0xa6, 0xb5, 0xf2, 0x54, 0x1e, 0x39, 0x76, 0x90, 0x30, 0x14, 0x15, 0x95, 0x28, 0x17, 0x8a, 0x61, 0x6a, 0x18, 0x5c, 0xf4, 0x8d, 0x76, 0xe5, 0x68, 0x79, 0x2c, 0x9b, 0xbf, 0x47, 0xfe, 0x39, 0xb3, 0x3b, 0xf9, 0x95, 0x27, 0xe3, 0xcf, 0xcd, 0x7e, 0x42, 0x82, 0x38, 0xd0, 0xd6, 0x8e, 0x55, 0x8d, 0x31, 0xe3, 0xd8, 0x40, 0x26, 0xd2, 0x2e, 0xee, 0x3f, 0x3e, 0x19, 0xdf, 0xcd, 0x4d, 0xfb, 0xd0, 0x89, 0xbc, 0xbe, 0xdd, 0x24, 0x5, 0xe0, 0x48, 0x5b, 0x17, 0xfb, 0x65, 0xb5, 0x14, 0xb7, 0x13, 0xc0, 0x8f, 0xcc, 0xd6, 0xe4, 0xe7, 0x3a, 0x86, 0x4e, 0x66, 0xf6, 0xff, 0x42, 0xfc, 0x91, 0xe4, 0x4f, 0x9, 0xca, 0x9f, 0x44, 0xc4, 0x70, 0x8c, 0x18, 0xa6, 0xe2, 0x5e, 0xe2, 0x46, 0x51, 0xb4, 0x17, 0xf4, 0xac, 0xb5, 0x81, 0xd2, 0xa4, 0x5d, 0x67, 0x4a, 0xf, 0xe2, 0xc0, 0x8b, 0x67, 0xe9, 0xa3, 0xdf, 0xc7, 0xf0, 0x61, 0x47, 0xfd, 0x71, 0x29, 0x4e, 0xfe, 0x3a, 0x89, 0x1f, 0xf7, 0x1f, 0x5f, 0x9d, 0x8a, 0x4a, 0xbf, 0x6, 0x65, 0xab, 0x33, 0x35, 0x1c, 0xcd, 0xa, 0xc6, 0x58, 0xe4, 0x31, 0xc9, 0x9f, 0xf0, 0xb6, 0x82, 0xf2, 0xf0, 0x23, 0x3a, 0x87, 0x4f, 0x9a, 0x55, 0xc9, 0x6f, 0x1f, 0x6c, 0xc0, 0xfe, 0x85, 0xe2, 0x8b, 0x3b, 0x23, 0xa3, 0x7c, 0xae, 0x35, 0x6e, 0x9e, 0x97, 0x36, 0x1a, 0x3f, 0xf, 0x5e, 0x1, 0x7c, 0x32, 0x76, 0x67, 0x1f, 0xd9, 0x35, 0x76, 0x34, 0x2f, 0x6f, 0x96, 0x7b, 0xc6, 0x95, 0xad, 0x40, 0xf0, 0x94, 0xb4, 0x1e, 0x6d, 0x95, 0xa1, 0x59, 0xb4, 0xf4, 0x3f, 0x86, 0x7d, 0xf7, 0x56, 0x89, 0x87, 0x2, 0x67, 0xa, 0x51, 0x1a, 0x1d, 0x33, 0x26, 0xce, 0x5c, 0x9e, 0xa4, 0x5e, 0x11, 0xab, 0xb3, 0x8f, 0xe8, 0xfa, 0xd3, 0x11, 0x54, 0x7b, 0x27, 0xca, 0x36, 0x83, 0x93, 0x6d, 0xb3, 0x87, 0xe, 0x20, 0x2d, 0x33, 0x47, 0xfa, 0xed, 0xd5, 0xea, 0xcc, 0x4f, 0xfe, 0x70, 0x8e, 0x2d, 0xeb, 0x1e, 0xdf, 0x76, 0x90, 0xb6, 0xae, 0x38, 0x44, 0xca, 0x89, 0x1a, 0xf7, 0x72, 0x31, 0xf8, 0xb9, 0x78, 0x9d, 0xed, 0x1a, 0x3b, 0x82, 0x6a, 0xff, 0x30, 0xca, 0xf6, 0x7a, 0xa, 0x2b, 0x4a, 0x5a, 0x6b, 0x84, 0xe5, 0xa6, 0x5b, 0xd0, 0xd3, 0x56, 0x2d, 0xcc, 0xdc, 0x65, 0x7f, 0x38, 0x4b, 0x91, 0xce, 0xd1, 0xb6, 0x82, 0x12, 0x1f, 0x94, 0xa5, 0x38, 0x28, 0x5f, 0xae, 0x81, 0x13, 0xc1, 0x38, 0xb9, 0x69, 0x8f, 0xd3, 0xd9, 0xa3, 0x98, 0xf9, 0x5f, 0xa2, 0xda, 0xc7, 0x41, 0x12, 0x55, 0x55, 0xf4, 0x75, 0x2b, 0x84, 0xfb, 0xe1, 0x31, 0xf3, 0x9a, 0xe8, 0xeb, 0x90, 0x17, 0xc8, 0xe5, 0x58, 0xf4, 0xdd, 0xc4, 0xd3, 0xc7, 0x69, 0x33, 0x6b, 0xe6, 0x8f, 0x3c, 0x83, 0xfd, 0x76, 0x19, 0xb6, 0x19, 0x13, 0xa1, 0xaa, 0x20, 0xc2, 0x3f, 0x21, 0x7a, 0x4c, 0xc0, 0x6f, 0x66, 0x39, 0x68, 0xff, 0x77, 0x72, 0x7c, 0x3f, 0x13, 0x61, 0x5, 0x13, 0x62, 0x27, 0x5b, 0xaf, 0x1e, 0x73, 0xf1, 0xfa, 0x65, 0xb7, 0xcf, 0xf9, 0x3b, 0xb2, 0x1d, 0xc3, 0x4f, 0x8f, 0x69, 0xd9, 0x1f, 0x3b, 0x8e, 0x4, 0x7c, 0x27, 0xdd, 0x5a, 0x99, 0xc8, 0x60, 0x1c, 0x55, 0xef, 0x3a, 0x2a, 0xfd, 0x43, 0xa3, 0xcd, 0xe8, 0xb, 0xa6, 0x5c, 0xfa, 0x8a, 0x40, 0x43, 0x65, 0x43, 0xa6, 0x7d, 0x68, 0x46, 0x7c, 0x35, 0xcc, 0xb4, 0xd, 0x7e, 0x8a, 0x80, 0x6f, 0xd4, 0x76, 0x82, 0x97, 0x74, 0xb1, 0xb5, 0xb6, 0xcf, 0xb5, 0x10, 0x59, 0x81, 0xec, 0x7f, 0x7b, 0xdf, 0x62, 0x46, 0x8f, 0xce, 0x76, 0x78, 0xf0, 0x5d, 0xe6, 0x1c, 0xa, 0xf0, 0x6, 0xe2, 0xf1, 0x64, 0x92, 0xfe, 0x12, 0x72, 0xf3, 0x56, 0xda, 0xc7, 0x30, 0x76, 0x78, 0xc, 0xc9, 0x1f, 0x3d, 0x81, 0x7d, 0xee, 0x93, 0xcc, 0xfe, 0xa0, 0xb2, 0xe9, 0x83, 0x73, 0xf0, 0x54, 0xa5, 0x9d, 0x14, 0x4a, 0x74, 0x86, 0x67, 0xd6, 0x16, 0x9c, 0xed, 0x79, 0xf4, 0xbb, 0x6e, 0x8a, 0x7e, 0xcd, 0x19, 0xef, 0xfd, 0xcf, 0xd1, 0xe8, 0x9f, 0xeb, 0xcd, 0xca, 0xfd, 0x3e, 0x29, 0xeb, 0x31, 0xc1, 0x57, 0xdd, 0xb2, 0xa4, 0xdf, 0x49, 0xe2, 0x4f, 0xfd, 0x7f, 0xb6, 0xe8, 0x18, 0x60, 0xc7, 0x61, 0xe4, 0xe7, 0x70, 0xf2, 0x73, 0x24, 0xd7, 0x1f, 0x66, 0xdd, 0x61, 0xaa, 0xea, 0x78, 0xe8, 0xe4, 0xe6, 0x62, 0x42, 0x3b, 0x99, 0xc3, 0x49, 0x8d, 0xf4, 0xe3, 0x45, 0x2, 0x21, 0x7d, 0x25, 0x38, 0xb0, 0x87, 0xc0, 0x2c, 0xcd, 0x52, 0x94, 0xa9, 0x96, 0xca, 0x91, 0x53, 0x91, 0x78, 0x66, 0x1a, 0x1, 0x1e, 0x2a, 0xa2, 0x77, 0x65, 0x78, 0xb5, 0xf2, 0x6d, 0xb2, 0x85, 0x11, 0xdb, 0x7d, 0xe8, 0xfc, 0xcd, 0x74, 0xef, 0x73, 0x4f, 0xcf, 0xba, 0xc6, 0xc, 0xb9, 0x15, 0xae, 0xdb, 0x30, 0x4, 0xc4, 0x3c, 0x6, 0xa3, 0xf, 0x91, 0xbf, 0x33, 0xae, 0x8d, 0xb8, 0x46, 0x85, 0xce, 0x2b, 0x19, 0xd3, 0xf6, 0x6c, 0xa5, 0xf2, 0xef, 0x63, 0x39, 0xfc, 0x44, 0xae, 0x73, 0xec, 0xc4, 0x98, 0xb6, 0xbe, 0x2c, 0x85, 0xf6, 0x1b, 0x96, 0xfc, 0x85, 0x2c, 0xf9, 0x7b, 0xfd, 0x19, 0xea, 0xdb, 0x22, 0x31, 0x89, 0xf0, 0x25, 0x2a, 0x46, 0x35, 0xe3, 0x25, 0x2b, 0xc9, 0xd0, 0xdf, 0x1f, 0xf5, 0xb6, 0xbf, 0x1e, 0x3d, 0xbd, 0x77, 0xb9, 0x1d, 0x23, 0xd7, 0x63, 0xc8, 0x9d, 0xbe, 0x71, 0xfe, 0xb2, 0x2c, 0xc7, 0x53, 0xc9, 0x1, 0x3a, 0x7d, 0x1b, 0x24, 0x50, 0x9b, 0x28, 0x84, 0xb9, 0x24, 0xea, 0x57, 0xd8, 0xfb, 0x4a, 0xf6, 0xc8, 0x93, 0xeb, 0xf4, 0x39, 0x45, 0xe1, 0x7f, 0x94, 0xc2, 0xfa, 0x1b, 0xb2, 0x17, 0xc1, 0x2e, 0x3f, 0xf1, 0xa2, 0xd7, 0xd7, 0x6d, 0xdd, 0xc7, 0xc0, 0xaf, 0xa1, 0xc5, 0xe8, 0x7f, 0xf3, 0xcc, 0xb8, 0xdb, 0xed, 0x1c, 0x7d, 0x3e, 0x6, 0x39, 0x30, 0x41, 0xa0, 0x9d, 0x69, 0x46, 0xb9, 0x48, 0x5f, 0xec, 0xd2, 0x7d, 0xdd, 0xee, 0xa6, 0x8, 0x16, 0xc3, 0xed, 0x30, 0x44, 0x22, 0xbf, 0xc9, 0xc, 0x7e, 0x1d, 0xab, 0x43, 0x9a, 0xfe, 0x85, 0xf4, 0x4f, 0xcd, 0x75, 0xfd, 0xe9, 0x44, 0x38, 0xee, 0x91, 0x6f, 0xfd, 0xcb, 0x89, 0x8c, 0x9d, 0x41, 0xb1, 0x5c, 0xc4, 0xf8, 0xe3, 0xd8, 0x47, 0xdf, 0xc3, 0xf1, 0x0, 0xbf, 0xbb, 0x9, 0xee, 0x67, 0xc6, 0xaf, 0xf7, 0x65, 0x83, 0xa9, 0x57, 0x8f, 0x49, 0xbf, 0x7e, 0x4c, 0x79, 0xf4, 0x27, 0xb0, 0xe5, 0x7f, 0xed, 0x9c, 0x3, 0x90, 0x2c, 0x49, 0x13, 0x80, 0xfb, 0xff, 0xcf, 0xb6, 0x6d, 0x6b, 0x7a, 0xe6, 0x6c, 0xdb, 0xb6, 0x6f, 0x7d, 0xb6, 0x6d, 0xdb, 0x76, 0xe0, 0x7c, 0xb7, 0xec, 0x99, 0xd5, 0xd9, 0xb6, 0x6d, 0x63, 0xdf, 0x54, 0xdd, 0x57, 0xbd, 0x39, 0x13, 0xf5, 0x2a, 0x5e, 0x5f, 0xaf, 0x63, 0xd0, 0x19, 0xf1, 0x45, 0x66, 0x55, 0x67, 0xd6, 0x54, 0x67, 0x66, 0xf5, 0xcc, 0xf2, 0x8a, 0xb5, 0x8f, 0x7b, 0xf9, 0x7f, 0x5e, 0x29, 0x8, 0x49, 0x9a, 0x8d, 0xd, 0x75, 0x93, 0x18, 0x4d, 0x57, 0x2, 0x9b, 0x24, 0x19, 0x46, 0x8f, 0xd, 0x4f, 0x58, 0xc8, 0x9c, 0xec, 0xc1, 0xb5, 0xd1, 0x36, 0xbf, 0x11, 0xf3, 0x1, 0x7b, 0x7f, 0x9e, 0x2f, 0x61, 0x7b, 0xfd, 0x9a, 0x27, 0x72, 0x7e, 0x4d, 0x73, 0x0, 0x1d, 0x8c, 0x3, 0x4e, 0x79, 0x27, 0xf4, 0x72, 0xfd, 0x65, 0x7c, 0xbf, 0x2, 0xed, 0xc2, 0x9a, 0xb2, 0xee, 0xf0, 0xef, 0x1, 0xed, 0xe2, 0xee, 0x3d, 0xf, 0x9a, 0x3d, 0x35, 0xaf, 0x50, 0xd3, 0x3c, 0xaf, 0x57, 0x2a, 0xc2, 0x9, 0x9a, 0x88, 0x6f, 0x0, 0xad, 0x27, 0x49, 0x51, 0x6, 0x29, 0x48, 0x29, 0x61, 0xf6, 0x55, 0xb4, 0xad, 0xf9, 0x62, 0x31, 0xa3, 0xe6, 0x42, 0x6d, 0xc7, 0x5a, 0xf1, 0x63, 0x75, 0xaf, 0x92, 0x57, 0x63, 0x77, 0xfa, 0x75, 0xad, 0xa5, 0xf7, 0xb, 0xb2, 0x3c, 0x5, 0x26, 0x81, 0x73, 0xe9, 0x4e, 0x1d, 0x52, 0xd3, 0xac, 0x38, 0x49, 0x46, 0x6b, 0xdf, 0x82, 0x31, 0xd8, 0x63, 0x57, 0x8b, 0x1d, 0xed, 0x67, 0x13, 0xbd, 0x66, 0xc4, 0x7a, 0xae, 0x2f, 0x7b, 0xb5, 0xed, 0x22, 0xe2, 0x8f, 0x4d, 0xb1, 0x5d, 0x3b, 0x6a, 0xad, 0x8, 0x7c, 0x8b, 0xb8, 0x78, 0xd7, 0x76, 0xf7, 0xc6, 0x41, 0x3b, 0xdc, 0x2b, 0x55, 0xf1, 0xeb, 0xda, 0xe6, 0xe0, 0xfd, 0xf2, 0x2d, 0xbf, 0xb6, 0x7f, 0xd3, 0xe8, 0xf0, 0xb4, 0x98, 0xb1, 0x20, 0x36, 0x14, 0x1b, 0x84, 0x64, 0xcb, 0x1c, 0x36, 0x73, 0x85, 0x58, 0x87, 0x1a, 0xc7, 0x76, 0xfd, 0xa2, 0xfd, 0x21, 0xde, 0xdf, 0xf2, 0x8b, 0x88, 0x8f, 0xb0, 0x23, 0xe7, 0x6c, 0x86, 0xb5, 0x17, 0xb0, 0x1b, 0xa0, 0xf5, 0x68, 0xaf, 0x94, 0xc5, 0xaf, 0x6d, 0xdb, 0x9a, 0xee, 0xcd, 0x83, 0xa6, 0x19, 0x14, 0x68, 0x6c, 0x25, 0xe3, 0xe2, 0x9c, 0xcc, 0x17, 0x35, 0x28, 0xeb, 0x7a, 0xc1, 0x4e, 0x0, 0x2b, 0x87, 0xe8, 0x96, 0xe7, 0x68, 0x82, 0x75, 0xbc, 0x52, 0x96, 0x15, 0x6a, 0x5a, 0xae, 0x5e, 0x81, 0x6e, 0x75, 0xb, 0x2b, 0x63, 0x80, 0xda, 0x96, 0xf7, 0x79, 0x62, 0xdc, 0x2, 0xa7, 0x63, 0x37, 0x33, 0x37, 0xc1, 0xe6, 0x18, 0x3e, 0xad, 0x15, 0x51, 0x7c, 0x3b, 0x87, 0x34, 0xc0, 0xcd, 0x2b, 0x36, 0x6, 0x93, 0x78, 0xa5, 0x2a, 0xa9, 0xba, 0xf6, 0x29, 0xd9, 0x64, 0x1b, 0xdd, 0x3a, 0xe, 0xb4, 0xc5, 0x8f, 0x34, 0x47, 0x3b, 0xfa, 0x4a, 0xa, 0xbf, 0x1b, 0xdf, 0xc4, 0x98, 0x3c, 0xf4, 0xaf, 0x6d, 0x5b, 0x6, 0xff, 0x5e, 0x8a, 0x55, 0xf4, 0x35, 0x36, 0x28, 0x40, 0x27, 0x80, 0x22, 0x2f, 0xa, 0x6d, 0xe, 0xcf, 0x47, 0x3c, 0x69, 0xf7, 0xf0, 0x4a, 0x59, 0xd2, 0xf5, 0x1d, 0x4b, 0x52, 0xe4, 0x4f, 0x29, 0xec, 0x57, 0x14, 0xbd, 0x83, 0x8d, 0x3f, 0xc8, 0xd7, 0xd6, 0x27, 0x31, 0x3f, 0x47, 0x84, 0xff, 0xbc, 0xf8, 0xe6, 0xf0, 0xd5, 0x7c, 0xca, 0xd, 0xe1, 0x46, 0x8d, 0x56, 0xa0, 0xa3, 0x69, 0xb3, 0xec, 0x61, 0xfb, 0x45, 0xf8, 0xb6, 0xb9, 0xf6, 0x28, 0xd2, 0x16, 0xf7, 0x1a, 0xca, 0xe4, 0x65, 0x85, 0x9a, 0xd6, 0x47, 0x39, 0x68, 0x53, 0x94, 0x7a, 0x13, 0x9c, 0xe5, 0xd7, 0xb5, 0xd7, 0xfb, 0xf5, 0x1d, 0x53, 0xf, 0xec, 0x43, 0x64, 0xc7, 0x64, 0x74, 0x76, 0x6f, 0x4a, 0xba, 0x5e, 0x92, 0xa1, 0x52, 0x62, 0xa7, 0x1d, 0x7c, 0x87, 0xb4, 0xad, 0x23, 0x89, 0x8f, 0x77, 0x7d, 0x5c, 0xff, 0x74, 0xd4, 0x5e, 0x22, 0xec, 0x91, 0xc6, 0xe4, 0x3, 0xbe, 0x25, 0xb7, 0x47, 0x7a, 0x15, 0x25, 0xf2, 0x6b, 0xe5, 0xdc, 0x5c, 0x2b, 0x9d, 0xfe, 0xb, 0xd0, 0x8, 0x61, 0x22, 0x55, 0x7f, 0x42, 0xdb, 0x1d, 0xda, 0x22, 0xc6, 0x68, 0x21, 0xca, 0xd7, 0xf5, 0x89, 0x5f, 0xc7, 0x9d, 0x8b, 0x7e, 0xd, 0x77, 0x7e, 0xf0, 0x7b, 0x8a, 0x88, 0x91, 0x27, 0x3, 0x5a, 0xc9, 0x1, 0x9, 0x56, 0xa8, 0x6d, 0x9b, 0xcc, 0xab, 0x44, 0x49, 0xd7, 0x7, 0x17, 0xf3, 0xd9, 0xe0, 0xf3, 0xe2, 0x69, 0xaa, 0x6f, 0x57, 0xa0, 0xab, 0x1d, 0x72, 0xa1, 0xd0, 0xa, 0xad, 0xe1, 0x47, 0xde, 0x6, 0x4e, 0xf3, 0x2a, 0x55, 0x52, 0x75, 0x1d, 0x3b, 0xd2, 0x4, 0xaf, 0xf1, 0xa8, 0xcb, 0xf3, 0x76, 0xa2, 0xcb, 0x1d, 0xee, 0x43, 0xc5, 0xce, 0xc5, 0xc7, 0x2a, 0x6c, 0x2d, 0x7c, 0x4a, 0x3, 0x1c, 0xe6, 0x55, 0xb2, 0xf8, 0x75, 0xc1, 0xec, 0x7c, 0x86, 0x78, 0x51, 0x6e, 0x1e, 0x2, 0xd1, 0x36, 0x81, 0x35, 0x3f, 0x78, 0xe2, 0xd7, 0x8, 0xe2, 0x63, 0x6d, 0x1b, 0xdc, 0x79, 0x50, 0x85, 0x42, 0x82, 0xf6, 0x43, 0x3b, 0xf4, 0x51, 0xc0, 0xb8, 0x5d, 0x45, 0xad, 0x6b, 0x62, 0xb0, 0x8d, 0xbf, 0x2, 0x6d, 0x48, 0xd5, 0xb6, 0x7f, 0x86, 0x6e, 0xf4, 0x2a, 0x5d, 0x32, 0xd, 0xd9, 0x9a, 0x74, 0x43, 0xf0, 0x85, 0x49, 0x18, 0x5a, 0x81, 0x2e, 0x17, 0xd8, 0xb3, 0xbd, 0x5f, 0x55, 0x98, 0x83, 0x71, 0xf0, 0x97, 0xc1, 0xf2, 0x53, 0x72, 0x8f, 0x2e, 0xca, 0x5e, 0x8f, 0x13, 0x6f, 0xf4, 0x97, 0xe4, 0xa5, 0xce, 0xab, 0x6, 0xf1, 0xeb, 0x83, 0xde, 0x74, 0x43, 0x56, 0xa3, 0x35, 0xda, 0x5, 0x2, 0x21, 0x1b, 0x47, 0x7c, 0xbc, 0xd8, 0xf1, 0xd7, 0x5d, 0xb2, 0x36, 0x4a, 0xd0, 0xb2, 0xef, 0xb0, 0xb0, 0x42, 0x61, 0x3e, 0xc8, 0x34, 0xe6, 0x56, 0x46, 0xef, 0xce, 0x29, 0x2e, 0x36, 0x8a, 0x1b, 0x67, 0x8f, 0xa5, 0x71, 0xcc, 0xfc, 0xab, 0xb0, 0xb9, 0x57, 0x2d, 0x42, 0xa7, 0xbf, 0x1, 0xe1, 0xa3, 0xd2, 0xe8, 0x92, 0xa4, 0x71, 0xbc, 0xb1, 0x12, 0x74, 0x61, 0xdf, 0xf2, 0xa8, 0x37, 0xcd, 0x92, 0xa3, 0x78, 0x1b, 0x52, 0xd0, 0x49, 0x3c, 0x11, 0x1a, 0x61, 0x43, 0xae, 0xfd, 0xa, 0xda, 0x8d, 0xb3, 0xc6, 0xf9, 0xfe, 0xf8, 0x6c, 0x2b, 0xfe, 0x19, 0xaf, 0x5a, 0xc4, 0xaf, 0xcf, 0x2e, 0x44, 0xc2, 0xde, 0x4f, 0x87, 0x49, 0xce, 0x29, 0xd0, 0x0, 0xe1, 0xd8, 0x21, 0x5b, 0x20, 0x62, 0xce, 0xbd, 0x1e, 0x45, 0xd6, 0xd6, 0x11, 0x36, 0x38, 0x31, 0xec, 0x51, 0x59, 0xb1, 0x8a, 0x62, 0xcb, 0x7c, 0x0, 0xd9, 0x7, 0x33, 0x8d, 0x9d, 0xab, 0xa5, 0x1b, 0x73, 0x53, 0xa4, 0x9b, 0x7a, 0xfe, 0xe7, 0x39, 0x92, 0x6e, 0xc8, 0x2d, 0xcc, 0x7d, 0x3e, 0x81, 0x9f, 0xac, 0x93, 0x1b, 0x6f, 0x2d, 0x59, 0xff, 0x46, 0xbf, 0x21, 0xbb, 0xa0, 0x57, 0x4d, 0xc2, 0xd, 0x6f, 0xc6, 0x8d, 0x7f, 0x26, 0xd, 0x60, 0x35, 0x41, 0x3c, 0xc4, 0x28, 0xdb, 0x6, 0xfb, 0x9a, 0xd1, 0x23, 0x8e, 0xfd, 0xf8, 0x67, 0xfc, 0x1, 0x45, 0x3f, 0x97, 0xa2, 0x2f, 0xa, 0x53, 0x31, 0xfe, 0xcf, 0xdf, 0xc8, 0xc5, 0x67, 0x16, 0x1a, 0xe1, 0x60, 0xeb, 0xad, 0x4e, 0x89, 0x86, 0xdc, 0xe1, 0xe9, 0xc6, 0xce, 0x99, 0xbc, 0x6a, 0x13, 0x1a, 0xe0, 0x4c, 0x6e, 0xfe, 0x67, 0x93, 0x5c, 0xb4, 0x26, 0xa1, 0x83, 0x82, 0x98, 0x3c, 0xd8, 0x63, 0x83, 0x72, 0xfc, 0x54, 0x41, 0x8b, 0xaf, 0x3b, 0x1f, 0x8b, 0xb3, 0x7e, 0x1f, 0xc5, 0x5a, 0x3c, 0xd3, 0xd4, 0x35, 0x27, 0x7a, 0x1a, 0x6f, 0x10, 0x42, 0xcc, 0x14, 0xc4, 0x2c, 0xc4, 0x1a, 0x9f, 0x81, 0xe6, 0x9, 0xf8, 0x8d, 0xdf, 0x90, 0x5b, 0xdb, 0x97, 0xb7, 0x8c, 0xaa, 0x13, 0x6e, 0xbe, 0x9b, 0x93, 0x91, 0xcf, 0x34, 0x75, 0x6a, 0x50, 0xa2, 0xe3, 0x50, 0x5, 0x88, 0xd5, 0x24, 0xf4, 0x26, 0xd8, 0x1f, 0xee, 0x66, 0xfc, 0x17, 0xe8, 0x28, 0xec, 0x78, 0x6b, 0x1c, 0xb, 0xbe, 0xca, 0xb2, 0xff, 0xf1, 0x86, 0x29, 0x34, 0xc2, 0x42, 0xec, 0x77, 0x7, 0x58, 0xd2, 0xab, 0x66, 0x21, 0x1, 0x5f, 0x83, 0x26, 0x21, 0xa, 0xb4, 0xd8, 0x91, 0x70, 0xdd, 0x1d, 0xbf, 0x97, 0x6e, 0xec, 0x4a, 0xc1, 0xc4, 0x8c, 0xe7, 0xce, 0x34, 0x75, 0xa7, 0x60, 0x4d, 0xc6, 0xeb, 0xc3, 0x26, 0xb0, 0x23, 0x1c, 0x83, 0xdf, 0xb5, 0x70, 0x3f, 0x3c, 0xb, 0xda, 0x7e, 0xcd, 0x81, 0x42, 0x8c, 0x2, 0x4d, 0xd3, 0xfe, 0x63, 0x5e, 0xcf, 0x1b, 0xa6, 0xac, 0x74, 0x48, 0x2f, 0x6b, 0x54, 0xb9, 0x64, 0x1a, 0xbb, 0x2e, 0xee, 0x2f, 0x8, 0x49, 0x86, 0x15, 0xad, 0xa2, 0x60, 0xbb, 0x28, 0x8, 0xfd, 0xf0, 0x57, 0x60, 0xfc, 0xb6, 0x49, 0xd5, 0x65, 0x23, 0xdf, 0x7b, 0xcd, 0x7, 0x32, 0x7c, 0xa6, 0x85, 0x39, 0x61, 0x3e, 0xe0, 0x4b, 0xb3, 0xce, 0x3e, 0x7b, 0x2d, 0x74, 0x2c, 0xe2, 0xab, 0x88, 0xd5, 0xd0, 0x37, 0x12, 0xd, 0x90, 0x8, 0x42, 0x22, 0xe7, 0xe2, 0xc4, 0x6e, 0x4b, 0x13, 0x3c, 0x16, 0x16, 0xfd, 0xe0, 0xee, 0x2, 0xca, 0x1a, 0x2b, 0x6b, 0xac, 0xc, 0x72, 0x8a, 0xcf, 0x26, 0x7e, 0xf2, 0x41, 0xbe, 0xde, 0xc4, 0x14, 0xb2, 0x93, 0xb5, 0xf2, 0xb2, 0x5e, 0x34, 0x4d, 0xe0, 0xec, 0x47, 0xf8, 0x91, 0x3d, 0x27, 0xd, 0x30, 0xc2, 0x8d, 0xb0, 0x14, 0x49, 0xdd, 0x1b, 0x9e, 0x0, 0xd, 0x24, 0xbd, 0xc7, 0xa0, 0x84, 0xd0, 0x66, 0xde, 0xa0, 0xe1, 0x29, 0x62, 0x66, 0xf6, 0x6, 0x29, 0xac, 0x31, 0x9, 0xd4, 0x12, 0x3b, 0x4e, 0x5e, 0x43, 0xd6, 0x8e, 0xc7, 0xf8, 0x13, 0x97, 0x47, 0xbf, 0x4, 0x13, 0x79, 0xa3, 0x22, 0x49, 0x23, 0x2c, 0xb, 0xfb, 0x92, 0xe0, 0xdb, 0xe0, 0x67, 0xd0, 0x16, 0x4a, 0x8a, 0xf0, 0x3d, 0xac, 0x31, 0xf4, 0xf, 0x60, 0x3d, 0x73, 0x11, 0xaf, 0xad, 0x6, 0x80, 0x81, 0x35, 0x0, 0xfc, 0x43, 0xec, 0x9d, 0xf0, 0x7f, 0x6f, 0xf4, 0x24, 0x91, 0x74, 0x53, 0xf7, 0x1c, 0xb0, 0x3f, 0x9c, 0x43, 0xc1, 0x3a, 0x40, 0x67, 0x28, 0x2, 0xfa, 0x77, 0xe6, 0xf6, 0x1e, 0x81, 0xf5, 0x3b, 0x56, 0x3c, 0xb8, 0xb7, 0xf, 0x58, 0x93, 0x2, 0x1f, 0xd2, 0x4b, 0x91, 0x5, 0x6c, 0x1b, 0xae, 0x2b, 0x33, 0x2f, 0xfc, 0x49, 0xec, 0xd1, 0x90, 0x34, 0xc0, 0x58, 0x9, 0x45, 0xf0, 0xe1, 0x42, 0xb8, 0x1e, 0x4e, 0x1e, 0x89, 0x5f, 0x48, 0xa1, 0x80, 0x1b, 0xd2, 0x50, 0x7f, 0x80, 0x29, 0xb2, 0x12, 0xb4, 0x8c, 0x6d, 0x94, 0xc1, 0xcc, 0xb, 0x79, 0xd8, 0x4c, 0xfe, 0x34, 0xbd, 0x5c, 0x25, 0x11, 0x9a, 0xc0, 0x7c, 0xcb, 0xf6, 0x33, 0xb4, 0xa6, 0x98, 0xe3, 0xc1, 0xdc, 0x4, 0xc1, 0xff, 0x53, 0x74, 0x2b, 0x3e, 0x73, 0x7b, 0xe5, 0x2f, 0x89, 0xf0, 0x68, 0x3f, 0x8b, 0xd3, 0xdd, 0x27, 0x8f, 0x7f, 0x85, 0xfe, 0x1b, 0x7e, 0xc6, 0xfe, 0x1a, 0xfd, 0x21, 0xbc, 0xcc, 0x69, 0xf, 0xd0, 0xf, 0x33, 0x77, 0x2b, 0xf6, 0xfe, 0x2b, 0x1d, 0xfa, 0xe4, 0xd2, 0x5e, 0x65, 0x48, 0x22, 0x9c, 0xe4, 0xa9, 0x38, 0xd1, 0x77, 0xc2, 0x43, 0xd8, 0x77, 0xc0, 0x35, 0x70, 0x16, 0xff, 0x60, 0xf9, 0x70, 0xd8, 0x3, 0x7b, 0xd, 0xae, 0xcd, 0xe2, 0x95, 0xbd, 0x24, 0x92, 0x48, 0x22, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, 0xfc, 0xb, 0x31, 0x1b, 0x93, 0xfa, 0xac, 0xe1, 0x98, 0xc3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char mini_checkerboard_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc3, 0x0, 0x0, 0xe, 0xc3, 0x1, 0xc7, 0x6f, 0xa8, 0x64, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x7c, 0xf0, 0xe0, 0xc1, 0x7f, 0x6, 0x3c, 0x40, 0x5e, 0x5e, 0x1e, 0x9f, 0x34, 0x3, 0x13, 0x5e, 0x59, 0x22, 0xc0, 0xa8, 0x1, 0x83, 0xc1, 0x0, 0xc6, 0xff, 0xff, 0xff, 0xe3, 0x4d, 0x7, 0xf, 0x1f, 0x3e, 0xa4, 0xad, 0xb, 0x46, 0xd, 0x18, 0xc, 0x6, 0x0, 0x0, 0x6e, 0x41, 0xa, 0xba, 0x94, 0xaa, 0x47, 0x57, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x98, 0xa0, 0xbd, 0x0, 0x0, 0x0, 0x17, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0x0, 0x5, 0xff, 0xa1, 0x60, 0xa0, 0x4, 0x60, 0xc, 0x98, 0xc4, 0x0, 0x9, 0x0, 0x0, 0x44, 0x81, 0xef, 0x81, 0xc1, 0x26, 0x8e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_arrow_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x56, 0x75, 0x5c, 0xe7, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x98, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0xbd, 0x91, 0xb1, 0xd, 0xc2, 0x30, 0x10, 0x45, 0xdf, 0xd1, 0xb8, 0xf0, 0x2, 0xee, 0xdc, 0x87, 0x1, 0xd8, 0x24, 0xb5, 0xe9, 0x19, 0x87, 0x1e, 0xea, 0x6c, 0xc2, 0x0, 0xa4, 0xb5, 0xdc, 0x79, 0x1, 0x17, 0xae, 0x8e, 0x26, 0x48, 0x76, 0x14, 0x40, 0x48, 0x88, 0xdf, 0xfd, 0xf7, 0xef, 0x4b, 0x77, 0x3a, 0xf8, 0x85, 0x62, 0x8c, 0x21, 0xc6, 0x18, 0xb6, 0x32, 0x59, 0x83, 0x94, 0xd2, 0x5e, 0x55, 0x6f, 0x0, 0x22, 0x72, 0xf0, 0xde, 0xdf, 0xdb, 0x7c, 0xd7, 0x9a, 0x9c, 0xb3, 0x55, 0xd5, 0x9, 0xb0, 0x80, 0x55, 0xd5, 0x29, 0xe7, 0x6c, 0x5f, 0x16, 0x6a, 0xad, 0x67, 0x60, 0x68, 0xd0, 0xb0, 0xb0, 0x3f, 0xaa, 0x3b, 0x3a, 0xa5, 0x74, 0x51, 0xd5, 0xd0, 0xd, 0x88, 0x5c, 0xbd, 0xf7, 0xc7, 0xa7, 0xef, 0x6e, 0x30, 0xc6, 0x9c, 0x80, 0xb9, 0x41, 0xf3, 0xc2, 0xd8, 0x2c, 0x38, 0xe7, 0x8a, 0x88, 0x8c, 0x40, 0x1, 0x8a, 0x88, 0x8c, 0xce, 0xb9, 0xf2, 0x71, 0xcf, 0x77, 0x8f, 0xfb, 0x5a, 0xf, 0x28, 0x4a, 0x37, 0xff, 0x58, 0x46, 0x7b, 0x50, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x2, 0xdc, 0x4f, 0xb8, 0x9f, 0x80, 0x45, 0xf8, 0xa1, 0xf6, 0x83, 0x2f, 0xf, 0xbe, 0x3c, 0xd4, 0x46, 0x13, 0x7e, 0xc1, 0xfd, 0xe0, 0xea, 0x83, 0xff, 0x40, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xfa, 0xf9, 0x40, 0x41, 0x30, 0x7c, 0x38, 0x9f, 0x81, 0x12, 0x80, 0x69, 0x14, 0x61, 0xcb, 0x11, 0xce, 0xc5, 0xe5, 0x41, 0xc2, 0x0, 0x0, 0x95, 0x48, 0x37, 0x91, 0x1f, 0xec, 0x77, 0x5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_button_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x32, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xff, 0xff, 0xff, 0xbd, 0x3f, 0x83, 0xbb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x65, 0xb5, 0xdd, 0x88, 0x9f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x1, 0xb, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x8d, 0xd1, 0xd9, 0x56, 0xc2, 0x30, 0x10, 0x80, 0x61, 0xd6, 0xa2, 0xa8, 0x6c, 0x75, 0xdf, 0x10, 0x54, 0xa8, 0x82, 0x74, 0xd2, 0x4a, 0x49, 0x5b, 0xa4, 0xb5, 0x5a, 0x44, 0x10, 0x5c, 0xd0, 0xaa, 0x80, 0x8a, 0xcb, 0xfb, 0x3f, 0x83, 0x21, 0x24, 0x1c, 0x8e, 0x5e, 0xe8, 0x77, 0xfd, 0x9f, 0x93, 0x99, 0x89, 0xcf, 0x1f, 0x8, 0x86, 0xc2, 0x42, 0x84, 0x10, 0x66, 0x66, 0xa3, 0x73, 0xf3, 0xbe, 0x9f, 0x2, 0xb, 0xb1, 0x78, 0x22, 0x99, 0x12, 0x45, 0x71, 0x71, 0x69, 0x79, 0x25, 0xea, 0xff, 0x15, 0x4, 0x63, 0xab, 0x6b, 0x7b, 0xcc, 0x7e, 0x2e, 0x2f, 0x1d, 0x30, 0x87, 0x52, 0x21, 0xbf, 0xbe, 0x41, 0x82, 0x50, 0x7c, 0xb3, 0x78, 0x54, 0x1a, 0x93, 0x1, 0x29, 0x2a, 0x73, 0xac, 0x94, 0x35, 0x6d, 0x8b, 0x4, 0xe1, 0x84, 0x5c, 0xe2, 0xe4, 0xa, 0xd6, 0xb, 0x8c, 0xa1, 0x2b, 0xd8, 0x54, 0x48, 0x20, 0x24, 0xab, 0x27, 0x5c, 0xb5, 0x2, 0x39, 0x89, 0xa9, 0x19, 0x2a, 0x58, 0xa3, 0x20, 0x92, 0xb2, 0x4f, 0x39, 0xdb, 0x1, 0xe3, 0x8c, 0xa9, 0x9d, 0xbb, 0x60, 0x3a, 0xa3, 0x40, 0xac, 0x5f, 0x70, 0xf5, 0xe9, 0xc0, 0x20, 0x1, 0xd0, 0xa0, 0x71, 0xc9, 0x35, 0xca, 0x4d, 0x75, 0x6a, 0x86, 0xd6, 0x15, 0xa6, 0x4f, 0xb4, 0x3b, 0x5c, 0x1b, 0xa1, 0xeb, 0x1b, 0x46, 0xbf, 0x5, 0xed, 0xe, 0xd1, 0x21, 0xbb, 0xf7, 0x5c, 0xf7, 0xc1, 0xf2, 0x26, 0x6b, 0x7a, 0x4d, 0xeb, 0xd1, 0xa2, 0x6b, 0x3e, 0x3d, 0x73, 0xc5, 0x5e, 0x1f, 0x7b, 0xc, 0xe0, 0xc1, 0xcb, 0xab, 0x49, 0xf, 0xb5, 0xdd, 0x79, 0x63, 0x86, 0xbd, 0x77, 0xd, 0x33, 0xad, 0x41, 0xff, 0xe3, 0x33, 0x4d, 0x4f, 0xbd, 0x93, 0x19, 0x7e, 0x8d, 0xd9, 0x0, 0xee, 0x64, 0x6, 0xd7, 0x41, 0xd9, 0xdd, 0xff, 0x7c, 0xd6, 0x5f, 0xdf, 0xfd, 0xd, 0xbf, 0xaa, 0x55, 0xdf, 0x7c, 0xb2, 0x44, 0x90, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x2f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x3f, 0x5a, 0x5a, 0x5a, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x59, 0x59, 0x59, 0x2a, 0x2a, 0x30, 0x4b, 0x4b, 0x4b, 0x22, 0x22, 0x27, 0x35, 0x35, 0x35, 0x4a, 0x4a, 0x4a, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x56, 0x56, 0x56, 0x62, 0x62, 0x62, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x48, 0x48, 0x48, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x54, 0x54, 0x54, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x52, 0x52, 0x52, 0x42, 0x42, 0x42, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x51, 0x51, 0x51, 0x40, 0x40, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4f, 0x4f, 0x4f, 0x3f, 0x3f, 0x3f, 0x4d, 0x4d, 0x4d, 0x3e, 0x3e, 0x3e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4c, 0x4c, 0x4c, 0x3d, 0x3d, 0x3d, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4a, 0x4a, 0x4a, 0x3b, 0x3b, 0x3b, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x49, 0x49, 0x49, 0x3a, 0x3a, 0x3a, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x39, 0x39, 0x39, 0x47, 0x47, 0x47, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x46, 0x46, 0x46, 0xd3, 0xa7, 0xd4, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xec, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x8e, 0x5, 0x4e, 0x0, 0x31, 0x10, 0x45, 0xff, 0xef, 0x56, 0xc2, 0xe2, 0xee, 0x4e, 0x3c, 0xc8, 0xd, 0xb8, 0x38, 0xa7, 0xc0, 0xdd, 0xdd, 0x5d, 0xbb, 0x94, 0x4c, 0xd2, 0xc1, 0xdf, 0x4a, 0x47, 0x5e, 0x27, 0xc3, 0xc, 0x80, 0x64, 0x24, 0xb8, 0xc7, 0x4f, 0x2c, 0x6b, 0xd5, 0x90, 0xff, 0xdd, 0x23, 0x7e, 0xc1, 0xa2, 0xb6, 0x64, 0xad, 0x26, 0x82, 0xc6, 0xef, 0x45, 0xbc, 0xe3, 0x1, 0x2c, 0x69, 0x9b, 0xc8, 0x7f, 0x5, 0x36, 0x1e, 0x41, 0x84, 0xd6, 0x4, 0x25, 0x90, 0xb7, 0x39, 0x6c, 0x4c, 0x2f, 0xbe, 0x68, 0xdf, 0x13, 0xa1, 0x9e, 0xc8, 0xa4, 0xb7, 0xe7, 0x47, 0x93, 0x63, 0x1b, 0xf9, 0xdc, 0x8, 0x88, 0x60, 0xbf, 0x4, 0xff, 0xd2, 0x56, 0x93, 0xe3, 0xb7, 0xb2, 0xf6, 0x2c, 0x36, 0x1, 0x16, 0xf4, 0x5f, 0x42, 0xc4, 0x17, 0x8f, 0xb5, 0xc0, 0xa5, 0x8, 0x30, 0x5f, 0xc2, 0x5d, 0xcf, 0xc9, 0xd, 0x74, 0x87, 0x8b, 0x5e, 0x56, 0x22, 0x24, 0xf7, 0x6d, 0xc2, 0xd1, 0x80, 0x26, 0x27, 0x5d, 0x5b, 0x67, 0x7d, 0x2f, 0x80, 0x4d, 0xc9, 0x7f, 0x9, 0x63, 0xa7, 0x61, 0x23, 0xc7, 0x74, 0x3d, 0xf, 0xae, 0x41, 0x84, 0x6f, 0x13, 0xe6, 0x26, 0xfa, 0x36, 0x46, 0x73, 0xbc, 0xba, 0x3b, 0xd6, 0xca, 0x8, 0xd0, 0xd6, 0x36, 0x4e, 0x35, 0xeb, 0xad, 0xd7, 0xb0, 0x60, 0x72, 0xdc, 0xda, 0x9c, 0x2, 0x67, 0x76, 0x64, 0x82, 0x99, 0x9b, 0xb6, 0x80, 0xb0, 0x7e, 0x8d, 0xf6, 0x5a, 0xdd, 0xe1, 0xb5, 0xba, 0xba, 0xb1, 0x0, 0xcd, 0xc7, 0x50, 0x23, 0xeb, 0xfb, 0x7f, 0xb4, 0xc8, 0x22, 0x18, 0xdd, 0x0, 0xd5, 0xec, 0x4e, 0x53, 0xc6, 0x18, 0x44, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_button_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xb4, 0x50, 0x4c, 0x54, 0x45, 0x95, 0xa9, 0xb0, 0x92, 0xa7, 0xae, 0x8e, 0xa2, 0xa9, 0x8a, 0x9d, 0xa4, 0x85, 0x98, 0x9f, 0x80, 0x93, 0x9b, 0x7b, 0x8f, 0x96, 0x77, 0x8a, 0x92, 0x72, 0x86, 0x8c, 0x6e, 0x80, 0x88, 0x69, 0x7c, 0x84, 0x64, 0x77, 0x7f, 0x60, 0x72, 0x7a, 0x5b, 0x6e, 0x75, 0x56, 0x69, 0x71, 0xc8, 0xe3, 0xe7, 0xc8, 0xe2, 0xe7, 0xca, 0xe3, 0xe7, 0xce, 0xe6, 0xe9, 0xce, 0xe6, 0xea, 0xd0, 0xe6, 0xe9, 0xce, 0xe5, 0xea, 0xd0, 0xe6, 0xea, 0xce, 0xe5, 0xe9, 0xd0, 0xe5, 0xe9, 0xd3, 0xe7, 0xeb, 0xd4, 0xe7, 0xeb, 0xd9, 0xea, 0xed, 0xd7, 0xe9, 0xed, 0xd7, 0xea, 0xed, 0xdc, 0xec, 0xef, 0xdc, 0xeb, 0xef, 0xe0, 0xed, 0xf1, 0xdf, 0xee, 0xf1, 0xdf, 0xed, 0xf1, 0xe0, 0xee, 0xf1, 0xe3, 0xf0, 0xf2, 0xe2, 0xef, 0xf2, 0xe3, 0xef, 0xf2, 0xe6, 0xf1, 0xf3, 0xe8, 0xf2, 0xf5, 0xe8, 0xf3, 0xf4, 0xe8, 0xf2, 0xf4, 0xe8, 0xf3, 0xf5, 0xd6, 0x5a, 0x5b, 0xd4, 0x57, 0x58, 0xe5, 0x89, 0x89, 0xd5, 0x57, 0x59, 0xd5, 0x58, 0x59, 0xd5, 0x59, 0x5a, 0xd6, 0x59, 0x5a, 0xd6, 0x5a, 0x5c, 0xd7, 0x5b, 0x5c, 0xd7, 0x5b, 0x5d, 0xd8, 0x5c, 0x5d, 0xd8, 0x5c, 0x5e, 0xd8, 0x5d, 0x5f, 0xd9, 0x5d, 0x5f, 0xe8, 0x6c, 0x6e, 0xff, 0xff, 0xff, 0xa1, 0xe, 0x37, 0x8c, 0x0, 0x0, 0x0, 0x2c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xac, 0x80, 0x68, 0x47, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x3b, 0x39, 0xe, 0xf4, 0x6c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xa3, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x5d, 0xce, 0xdb, 0x16, 0x42, 0x60, 0x18, 0x84, 0xe1, 0x4f, 0x8, 0x65, 0x57, 0x2a, 0x45, 0xd9, 0x15, 0xf9, 0xa3, 0x89, 0x28, 0xea, 0xfe, 0x2f, 0x2c, 0x4e, 0x2c, 0x79, 0xd6, 0xbc, 0xe7, 0x43, 0x34, 0xc5, 0xe5, 0x7f, 0x38, 0x9a, 0xdd, 0x31, 0x72, 0x9f, 0x11, 0xff, 0x80, 0x61, 0x18, 0xa6, 0x69, 0xac, 0xba, 0x50, 0xf0, 0x24, 0x14, 0x58, 0x5b, 0x9b, 0xed, 0xce, 0xb6, 0x6d, 0x6b, 0x8f, 0x42, 0x20, 0xb1, 0xc4, 0xc1, 0x71, 0xe, 0x7d, 0x8e, 0x83, 0x52, 0xa4, 0xf9, 0x13, 0xae, 0x7b, 0x74, 0x7b, 0xa7, 0x13, 0x9e, 0x73, 0x92, 0x72, 0x78, 0xbe, 0xef, 0x75, 0x7c, 0xcf, 0x47, 0x2e, 0x91, 0x5c, 0x21, 0x8, 0xc2, 0x20, 0x8c, 0x82, 0x73, 0x18, 0xa1, 0x92, 0x49, 0xa9, 0x71, 0x89, 0xe3, 0x38, 0x49, 0xba, 0x25, 0xa8, 0x15, 0x5a, 0xbc, 0x70, 0x1d, 0xe0, 0xb5, 0xa0, 0xe5, 0x1b, 0x69, 0xca, 0xd8, 0x8d, 0x31, 0x96, 0x65, 0x68, 0x96, 0xa4, 0x36, 0x60, 0x3, 0x34, 0x2a, 0x69, 0xed, 0xf8, 0x7a, 0xab, 0x91, 0xfe, 0xf9, 0x8e, 0x7c, 0x74, 0xd2, 0x27, 0x7e, 0x7e, 0x46, 0x20, 0x17, 0xe, 0x2d, 0x4e, 0x9, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xb1, 0x50, 0x4c, 0x54, 0x45, 0x95, 0xa9, 0xb0, 0x92, 0xa7, 0xae, 0x8e, 0xa2, 0xa9, 0x8a, 0x9d, 0xa4, 0x85, 0x98, 0x9f, 0x80, 0x93, 0x9b, 0x7b, 0x8f, 0x96, 0x77, 0x8a, 0x92, 0x72, 0x86, 0x8c, 0x6e, 0x80, 0x88, 0x69, 0x7c, 0x84, 0x64, 0x77, 0x7f, 0x60, 0x72, 0x7a, 0x5b, 0x6e, 0x75, 0x56, 0x69, 0x71, 0xc8, 0xe3, 0xe7, 0xc8, 0xe2, 0xe7, 0xca, 0xe3, 0xe7, 0xce, 0xe6, 0xe9, 0xce, 0xe6, 0xea, 0xd0, 0xe6, 0xe9, 0xce, 0xe5, 0xea, 0xd0, 0xe6, 0xea, 0xce, 0xe5, 0xe9, 0xd0, 0xe5, 0xe9, 0xd3, 0xe7, 0xeb, 0xd4, 0xe7, 0xeb, 0xd9, 0xea, 0xed, 0xd7, 0xe9, 0xed, 0xd7, 0xea, 0xed, 0xdc, 0xec, 0xef, 0xdc, 0xeb, 0xef, 0xe0, 0xed, 0xf1, 0xdf, 0xee, 0xf1, 0xdf, 0xed, 0xf1, 0xe0, 0xee, 0xf1, 0xe3, 0xf0, 0xf2, 0xe2, 0xef, 0xf2, 0xe3, 0xef, 0xf2, 0xe6, 0xf1, 0xf3, 0xe8, 0xf2, 0xf5, 0xe8, 0xf3, 0xf4, 0xe8, 0xf2, 0xf4, 0xe8, 0xf3, 0xf5, 0xd6, 0x5a, 0x5b, 0xd4, 0x57, 0x58, 0xe5, 0x89, 0x89, 0xd5, 0x57, 0x59, 0xd5, 0x58, 0x59, 0xd5, 0x59, 0x5a, 0xd6, 0x59, 0x5a, 0xd6, 0x5a, 0x5c, 0xd7, 0x5b, 0x5c, 0xd7, 0x5b, 0x5d, 0xd8, 0x5c, 0x5d, 0xd8, 0x5c, 0x5e, 0xd8, 0x5d, 0x5f, 0xd9, 0x5d, 0x5f, 0xe8, 0x6c, 0x6e, 0x20, 0x6, 0x32, 0x78, 0x0, 0x0, 0x0, 0x2c, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xac, 0x80, 0x68, 0x47, 0x0, 0x0, 0x0, 0x72, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0xc8, 0xc1, 0x8d, 0xc2, 0x30, 0x18, 0x6, 0xd1, 0x99, 0xdf, 0x8e, 0x7c, 0x59, 0x6d, 0xb, 0x14, 0x40, 0xff, 0xa5, 0x50, 0x0, 0x6d, 0x4, 0xe4, 0xf, 0x59, 0x8, 0x42, 0xde, 0xf1, 0x35, 0xce, 0xd0, 0x2b, 0xbf, 0x6e, 0x5d, 0xe5, 0x10, 0xbb, 0x95, 0x3b, 0x48, 0x2a, 0xe4, 0xd2, 0xec, 0x9a, 0xe6, 0x2, 0xcf, 0xd, 0x56, 0x30, 0x24, 0x48, 0x6, 0xb8, 0x82, 0xc2, 0x0, 0xd6, 0x3b, 0xf6, 0x6a, 0x12, 0xc0, 0xc8, 0x6e, 0x77, 0x3c, 0xa, 0xa3, 0xb3, 0x4d, 0x19, 0x76, 0xa5, 0xb, 0x12, 0x1b, 0xb8, 0x82, 0xc6, 0x22, 0x7c, 0x42, 0xc4, 0x40, 0x41, 0x59, 0x8a, 0x42, 0x80, 0x39, 0xc1, 0xae, 0x6c, 0x7c, 0xb9, 0xe2, 0x8f, 0x43, 0xf4, 0x9f, 0xb3, 0x17, 0x98, 0xa8, 0x1a, 0xb6, 0xa7, 0xd9, 0xa6, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_button_hover_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x44, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xff, 0xff, 0xff, 0xd1, 0x85, 0xc5, 0x5, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6b, 0x52, 0x65, 0xa5, 0x98, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x1, 0x10, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x8d, 0xd1, 0xd9, 0x52, 0xc2, 0x30, 0x14, 0x80, 0x61, 0xd6, 0xa2, 0xa8, 0x6c, 0x75, 0xdf, 0x77, 0xa8, 0xa2, 0x70, 0xd2, 0x4a, 0x25, 0x6d, 0x81, 0x62, 0xa1, 0x88, 0x20, 0x6e, 0xc5, 0xaa, 0x80, 0xfb, 0xfa, 0xfe, 0xf, 0x60, 0x49, 0x13, 0x86, 0xd1, 0xb, 0xfd, 0x6e, 0xf3, 0xcf, 0xe4, 0x24, 0xc7, 0xe3, 0xf5, 0xf9, 0x3, 0x41, 0x2e, 0xe4, 0xe0, 0x46, 0x46, 0xc3, 0x63, 0xe3, 0x9e, 0x9f, 0x7c, 0x13, 0x91, 0x68, 0x2c, 0x9e, 0xe0, 0x79, 0x7e, 0x72, 0x6a, 0x7a, 0x26, 0xec, 0xfd, 0x15, 0xf8, 0x23, 0xb3, 0x73, 0x9b, 0x5b, 0xae, 0xed, 0x64, 0x4a, 0xd8, 0xa1, 0x76, 0x85, 0x74, 0x6a, 0x7e, 0xc1, 0x9, 0x2, 0xd1, 0xc5, 0xbd, 0xfd, 0x8c, 0x2b, 0xb, 0x48, 0x94, 0xa8, 0x3, 0x31, 0x27, 0xcb, 0x4b, 0x4e, 0x10, 0x8c, 0x1d, 0xb2, 0xf3, 0x4c, 0x36, 0x8f, 0x95, 0x34, 0xa5, 0x2a, 0x22, 0xd6, 0x44, 0x27, 0xe0, 0xe2, 0x85, 0x22, 0x53, 0xc8, 0x43, 0x52, 0xa0, 0x4a, 0xaa, 0x4, 0x7a, 0x3f, 0x8, 0x25, 0xca, 0x47, 0x4c, 0xd9, 0x0, 0xb5, 0x42, 0x95, 0xaa, 0x26, 0x68, 0x46, 0x3f, 0xe0, 0x6b, 0xc7, 0x4c, 0x6d, 0x38, 0x50, 0x9d, 0x0, 0x48, 0x50, 0x3f, 0x61, 0xea, 0xb9, 0x86, 0x34, 0x34, 0x43, 0xf3, 0x14, 0x93, 0x2b, 0x5a, 0x67, 0x4c, 0xb, 0xa1, 0xf3, 0xb, 0x4a, 0xb9, 0x4, 0xf9, 0xa, 0x91, 0x21, 0xad, 0x36, 0x63, 0x5d, 0xeb, 0xf6, 0xe0, 0x99, 0x76, 0x43, 0xbf, 0xd1, 0xc9, 0x33, 0xad, 0xf6, 0x2d, 0x75, 0xd7, 0xe9, 0x62, 0x9b, 0x2, 0xdc, 0xbb, 0x7f, 0xd0, 0xc8, 0x47, 0x2d, 0x3f, 0x3e, 0x3d, 0xbb, 0x5e, 0x3a, 0xaf, 0x32, 0xa6, 0x9a, 0xbd, 0xee, 0xdb, 0xfb, 0xa, 0xf9, 0xea, 0xd5, 0xb5, 0x8f, 0x4f, 0xd7, 0x17, 0x80, 0x39, 0x98, 0xc1, 0x34, 0xd0, 0xfa, 0xc6, 0x7f, 0x96, 0xf5, 0xd7, 0xba, 0xbf, 0x1, 0xfe, 0x22, 0x58, 0x7c, 0xf4, 0xd2, 0xd1, 0x68, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0x4b, 0x5f, 0x5a, 0x6c, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x5f, 0x5a, 0x6b, 0x2a, 0x2a, 0x30, 0x56, 0x53, 0x64, 0x22, 0x22, 0x27, 0x3e, 0x3b, 0x46, 0x57, 0x53, 0x63, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x5b, 0x57, 0x68, 0x5a, 0x56, 0x67, 0x67, 0x63, 0x76, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x4d, 0x4a, 0x57, 0x49, 0x46, 0x52, 0x48, 0x45, 0x51, 0x5a, 0x56, 0x65, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x5b, 0x57, 0x66, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x59, 0x55, 0x64, 0x47, 0x44, 0x50, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x58, 0x54, 0x64, 0x46, 0x43, 0x50, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x56, 0x53, 0x63, 0x45, 0x42, 0x4f, 0x56, 0x53, 0x62, 0x45, 0x42, 0x4e, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x55, 0x51, 0x62, 0x44, 0x41, 0x4e, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x55, 0x51, 0x60, 0x44, 0x41, 0x4d, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x43, 0x40, 0x4c, 0x54, 0x50, 0x5f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x47, 0x43, 0x51, 0x43, 0x3f, 0x4d, 0x42, 0x3f, 0x4c, 0x53, 0x4f, 0x5f, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x53, 0x50, 0x5f, 0x53, 0x4f, 0x5e, 0x5f, 0x5a, 0x6c, 0xd3, 0x26, 0x54, 0x35, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe5, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x85, 0x91, 0x43, 0x62, 0xc5, 0x60, 0x18, 0x45, 0xef, 0x8d, 0x51, 0xdb, 0xee, 0x46, 0xca, 0x6d, 0x77, 0x58, 0xce, 0x6b, 0xdb, 0x7c, 0x8a, 0xad, 0xea, 0x44, 0x1f, 0x4e, 0x92, 0x1f, 0x4c, 0x0, 0xe0, 0x9, 0x61, 0xf0, 0x89, 0x32, 0x12, 0xcd, 0xd4, 0x8, 0xef, 0x1f, 0x35, 0x54, 0xa0, 0x68, 0x1a, 0x34, 0x89, 0x8, 0x86, 0xa4, 0xd, 0x57, 0xb4, 0xdf, 0x79, 0x5, 0x89, 0x94, 0xba, 0xf9, 0xb3, 0xc0, 0xce, 0xeb, 0xf0, 0x17, 0x1c, 0xab, 0x11, 0x9, 0x1a, 0xf9, 0x96, 0x84, 0x5d, 0x5e, 0x43, 0x15, 0x7, 0x2e, 0x42, 0x41, 0x51, 0xd3, 0xbe, 0x67, 0xd5, 0x6b, 0x42, 0x3a, 0x38, 0x9b, 0xf5, 0x2e, 0x20, 0xfa, 0x5, 0x33, 0x41, 0x69, 0xf4, 0xeb, 0x49, 0x6c, 0x19, 0xe6, 0xbd, 0xdd, 0xd, 0x48, 0xa0, 0x92, 0xb, 0x36, 0x72, 0x6a, 0x26, 0xf0, 0x14, 0xa, 0x10, 0x72, 0xe1, 0x7d, 0xf4, 0xf6, 0x35, 0x1b, 0xc3, 0xe3, 0x18, 0x9d, 0x50, 0xf0, 0xe4, 0xc2, 0x17, 0xae, 0x27, 0xd3, 0xe4, 0x6e, 0xe8, 0xf8, 0x7e, 0xbc, 0x1, 0x48, 0x9e, 0x57, 0xf8, 0xc5, 0xfc, 0xbd, 0x7a, 0x98, 0xc4, 0x94, 0x47, 0xbf, 0xe4, 0xce, 0x48, 0x8, 0x6e, 0x69, 0x91, 0x63, 0x47, 0x73, 0x49, 0xbc, 0x77, 0x3e, 0xd7, 0x4b, 0x3b, 0x12, 0x36, 0x16, 0xb3, 0x85, 0x6a, 0x68, 0xce, 0x39, 0x62, 0xc6, 0xba, 0x3d, 0x8d, 0xe7, 0x91, 0x20, 0xac, 0xad, 0xa6, 0xc2, 0xe, 0x6, 0xcc, 0x74, 0xc, 0x2d, 0xe7, 0xf9, 0x55, 0x2, 0x28, 0x94, 0x37, 0xab, 0xee, 0xa1, 0xcc, 0xbf, 0xdb, 0xed, 0x3, 0x70, 0xe6, 0x4f, 0x4a, 0xc3, 0xed, 0xed, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_button_normal_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x44, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0xff, 0xff, 0xff, 0xd7, 0xc8, 0xfe, 0x88, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6b, 0x52, 0x65, 0xa5, 0x98, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x1, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x8d, 0xd1, 0xd7, 0x56, 0xc2, 0x40, 0x10, 0x80, 0x61, 0x6a, 0x50, 0x54, 0x5a, 0xec, 0xbd, 0x43, 0x14, 0x85, 0xdd, 0x44, 0xa2, 0x6c, 0x12, 0x20, 0x18, 0xd, 0x22, 0x8, 0x16, 0x34, 0x16, 0xc0, 0x5e, 0xde, 0xff, 0x1, 0x5c, 0x36, 0xb3, 0x1c, 0x8e, 0x5e, 0xe8, 0x77, 0xfd, 0x9f, 0xb3, 0x33, 0xb3, 0x1e, 0xaf, 0xcf, 0x1f, 0x8, 0xa, 0x21, 0x4a, 0x18, 0x1a, 0xe, 0x8f, 0x8c, 0x7a, 0x7e, 0xf2, 0x8d, 0x45, 0xa2, 0xb1, 0x78, 0x42, 0x14, 0xc5, 0xf1, 0x89, 0xc9, 0xa9, 0xb0, 0xf7, 0x57, 0xe0, 0x8f, 0x4c, 0xcf, 0xac, 0x6f, 0xb8, 0x36, 0x93, 0x29, 0x69, 0xb, 0x6c, 0x4b, 0xe9, 0xd4, 0xec, 0x1c, 0xd, 0x2, 0xd1, 0xf9, 0x9d, 0xdd, 0x8c, 0x2b, 0x8b, 0xb0, 0xac, 0x80, 0x3d, 0x39, 0xa7, 0xaa, 0xb, 0x34, 0x8, 0xc6, 0xb2, 0x99, 0x7d, 0x70, 0x90, 0x27, 0x5a, 0x1a, 0xe8, 0x9a, 0x4c, 0xc, 0x99, 0x6, 0x42, 0xbc, 0x50, 0xe4, 0xa, 0x79, 0x94, 0x94, 0x40, 0x49, 0x57, 0x90, 0xd9, 0xb, 0x42, 0x89, 0xf2, 0x21, 0x57, 0xb6, 0x90, 0x7e, 0x4, 0x4a, 0xc7, 0x36, 0x32, 0xac, 0x5e, 0x20, 0x56, 0x4e, 0xb8, 0xca, 0x60, 0xa0, 0xd3, 0x0, 0xb1, 0xa0, 0x7a, 0xca, 0x55, 0x73, 0x35, 0x65, 0x60, 0x86, 0xfa, 0x19, 0x61, 0x4f, 0x34, 0x9a, 0x5c, 0x3, 0xe3, 0xf3, 0xb, 0xa0, 0x5d, 0x22, 0xf5, 0xa, 0xb3, 0x21, 0x5b, 0xd7, 0x5c, 0xeb, 0xc6, 0x74, 0xfa, 0x6b, 0x3a, 0x35, 0xf3, 0xd6, 0x64, 0x6b, 0xde, 0xdd, 0x73, 0xf, 0xed, 0xe, 0x71, 0x0, 0x22, 0xdd, 0xc7, 0x27, 0x83, 0x1d, 0x6a, 0xb1, 0xf9, 0xc, 0x5e, 0xda, 0xaf, 0x2a, 0x1, 0xf5, 0x6e, 0xe7, 0xed, 0x7d, 0x89, 0x9d, 0x7a, 0x79, 0xe5, 0xe3, 0xd3, 0xf5, 0x85, 0x90, 0xdd, 0x9f, 0xc1, 0xb6, 0xf0, 0xea, 0xda, 0x7f, 0x3e, 0xeb, 0xaf, 0xef, 0xfe, 0x6, 0x1a, 0x96, 0x59, 0x89, 0x91, 0xf7, 0xf1, 0x7b, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x41, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x56, 0x52, 0x60, 0x2a, 0x2a, 0x30, 0x47, 0x44, 0x52, 0x22, 0x22, 0x27, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x5d, 0x5a, 0x6a, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x46, 0x42, 0x4f, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x4e, 0x4b, 0x58, 0x8, 0xd9, 0x10, 0xcb, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0x91, 0x53, 0x62, 0x5, 0x41, 0x14, 0x44, 0xab, 0x46, 0xb1, 0x6d, 0x67, 0x3, 0x59, 0x44, 0x16, 0x9e, 0xdf, 0xe8, 0x2f, 0xb6, 0xcd, 0xe7, 0x61, 0xe3, 0x3e, 0x4e, 0xb5, 0x2e, 0x4e, 0xdb, 0xa3, 0x11, 0x80, 0xc4, 0x51, 0xc6, 0x3f, 0x5a, 0xe5, 0xb1, 0x4f, 0x8, 0xd5, 0xff, 0x15, 0xd1, 0x26, 0xba, 0x7d, 0xbd, 0xec, 0xa3, 0x75, 0x94, 0x24, 0x11, 0xbb, 0xe1, 0x2f, 0x1f, 0xe0, 0x91, 0xde, 0x8, 0xf3, 0x1, 0xe, 0x3d, 0x42, 0x1, 0xe3, 0x49, 0xf, 0x1a, 0xc0, 0xb7, 0xb5, 0x47, 0x92, 0x52, 0xb7, 0x3b, 0x79, 0xa7, 0x80, 0x21, 0xc2, 0x2a, 0xa9, 0x15, 0x8b, 0x8e, 0x1c, 0x2e, 0x64, 0x71, 0x4, 0xd0, 0x5b, 0x34, 0x80, 0xa0, 0x34, 0x29, 0xab, 0xd5, 0x7a, 0xfb, 0x5e, 0xc2, 0x51, 0xc0, 0x3, 0x83, 0x6, 0x10, 0xa2, 0xa1, 0x62, 0x1f, 0xf0, 0xae, 0x0, 0x38, 0xd, 0xe0, 0x67, 0xfe, 0xe9, 0xb, 0x72, 0x86, 0xb7, 0x5, 0x46, 0xa, 0x48, 0xfc, 0xa6, 0x15, 0x1e, 0x96, 0xc5, 0x79, 0x99, 0xbe, 0x78, 0x59, 0x2c, 0x1, 0x5e, 0x92, 0x34, 0x6d, 0xb1, 0xf9, 0xda, 0x75, 0x66, 0x6d, 0xfa, 0xf3, 0x5, 0x7f, 0x58, 0x3, 0x8d, 0x15, 0xc8, 0x85, 0xf3, 0xd, 0x6b, 0x1f, 0xdf, 0x6e, 0x8c, 0x33, 0xd4, 0xc0, 0xce, 0xd6, 0xa8, 0x0, 0xd5, 0x20, 0xbc, 0xb5, 0xf6, 0xc2, 0x68, 0xb6, 0xf4, 0x8d, 0x6, 0x9c, 0xc3, 0x6d, 0x5a, 0x60, 0xf, 0x53, 0x7d, 0x72, 0x86, 0x6a, 0xf4, 0xf1, 0xed, 0x1, 0x74, 0x5a, 0x3f, 0xab, 0x94, 0xee, 0x3f, 0x7a, 0x64, 0x11, 0x8a, 0x6e, 0x0, 0x80, 0xdd, 0x4f, 0x5c, 0xe, 0xd7, 0x26, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char option_button_pressed_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x4d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0xff, 0xff, 0xff, 0x2, 0x4e, 0xff, 0xf1, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6e, 0x22, 0xf, 0x51, 0x17, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x1, 0xe, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x8d, 0xd1, 0x57, 0x53, 0xc2, 0x40, 0x10, 0xc0, 0x71, 0x6a, 0x50, 0x54, 0x5a, 0xec, 0xbd, 0x43, 0x14, 0xf5, 0x2e, 0x91, 0xa8, 0x5c, 0x12, 0x20, 0x18, 0xd, 0x22, 0x88, 0xd, 0x8d, 0xd, 0xb0, 0xd7, 0xef, 0xff, 0xea, 0x71, 0xd9, 0x63, 0x18, 0x7d, 0xd0, 0xdf, 0xf3, 0x7f, 0xe6, 0x76, 0xf7, 0x3c, 0x5e, 0x9f, 0x3f, 0x10, 0x14, 0x42, 0x94, 0xd0, 0xd3, 0x1b, 0xee, 0xeb, 0xf7, 0xfc, 0xe4, 0x1b, 0x88, 0x44, 0x63, 0xf1, 0x84, 0x28, 0x8a, 0x83, 0x43, 0xc3, 0x23, 0x61, 0xef, 0xaf, 0xc0, 0x1f, 0x19, 0x1d, 0x5b, 0x5c, 0x72, 0x2d, 0x27, 0x53, 0xd2, 0xa, 0x58, 0x95, 0xd2, 0xa9, 0xf1, 0x9, 0x1a, 0x4, 0xa2, 0x93, 0x6b, 0xeb, 0x60, 0x3, 0x61, 0x59, 0x1, 0x9b, 0x72, 0x46, 0x55, 0xa7, 0x68, 0x10, 0x8c, 0x6d, 0x6d, 0x73, 0x3b, 0x59, 0xa2, 0xa5, 0x81, 0xae, 0xc9, 0xc4, 0x90, 0x69, 0x20, 0xc4, 0x73, 0x79, 0x2e, 0x97, 0x45, 0x49, 0x9, 0x14, 0x74, 0x5, 0x99, 0xed, 0x20, 0x94, 0x28, 0xee, 0x72, 0x45, 0xb, 0xe9, 0x7b, 0xa0, 0xb0, 0x6f, 0x23, 0xc3, 0x6a, 0x7, 0x62, 0xe9, 0x80, 0x2b, 0x75, 0x7, 0x3a, 0xd, 0x10, 0xb, 0xca, 0x87, 0x5c, 0x39, 0x53, 0x51, 0xba, 0x66, 0xa8, 0x1e, 0x11, 0xf6, 0x44, 0xed, 0x98, 0xab, 0x61, 0x7c, 0x72, 0xa, 0xb4, 0x33, 0xa4, 0x9e, 0x63, 0x36, 0x64, 0xfd, 0x82, 0xab, 0x5f, 0x9a, 0x4e, 0x67, 0x4d, 0xa7, 0x62, 0x5e, 0x99, 0x6c, 0xcd, 0xeb, 0x9b, 0x5b, 0x70, 0xd7, 0x68, 0x12, 0x7, 0x20, 0xd2, 0xba, 0x7f, 0x30, 0xd8, 0xa1, 0xa6, 0x1f, 0x9f, 0x9e, 0x5d, 0x2f, 0x8d, 0x57, 0x95, 0x80, 0x6a, 0xab, 0xf9, 0xf6, 0x3e, 0xc3, 0x4e, 0x3d, 0x3b, 0xf7, 0xf1, 0xe9, 0xfa, 0x42, 0xc8, 0xee, 0xcc, 0x60, 0x5b, 0x78, 0x7e, 0xe1, 0x3f, 0x9f, 0xf5, 0xd7, 0x77, 0x7f, 0x3, 0x1c, 0x7f, 0x59, 0xc2, 0x5e, 0xdd, 0xbf, 0x43, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char panel_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x40, 0x5, 0x0, 0x0, 0x10, 0x0, 0x1, 0xa1, 0xc5, 0x21, 0xc1, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xa5, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x35, 0x32, 0x3b, 0xf, 0xeb, 0x7f, 0x60, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x36, 0x47, 0xbf, 0x88, 0xd1, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xaf, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x65, 0xcf, 0xc9, 0x12, 0x82, 0x30, 0x10, 0x4, 0xd0, 0x1, 0x12, 0xb6, 0x0, 0x21, 0x2c, 0xb2, 0xaa, 0xa0, 0xa8, 0x80, 0x88, 0x8, 0xf8, 0xff, 0xbf, 0x66, 0x98, 0x93, 0xa5, 0xef, 0xd8, 0x55, 0xd3, 0xd5, 0x3, 0xa0, 0xa8, 0x1a, 0xa1, 0xba, 0x44, 0x89, 0xa6, 0x2a, 0x0, 0x8a, 0x41, 0x4d, 0xcb, 0x66, 0x8e, 0xc3, 0x6c, 0xcb, 0xa4, 0x86, 0x2, 0x2a, 0x75, 0x3d, 0xee, 0x8b, 0x20, 0x10, 0x3e, 0xf7, 0x5c, 0xaa, 0x82, 0x66, 0x7a, 0x61, 0x14, 0xef, 0xa4, 0x38, 0xa, 0x3d, 0x53, 0x3, 0x62, 0xf1, 0xa4, 0xed, 0x50, 0x9b, 0x70, 0x8b, 0x0, 0xb5, 0xd3, 0xac, 0xeb, 0x51, 0x97, 0xa5, 0x36, 0x5, 0x9d, 0x89, 0xbc, 0xbf, 0xa3, 0x3e, 0x17, 0x4c, 0x7, 0xdd, 0x9, 0x8a, 0xe1, 0x81, 0x86, 0x22, 0x70, 0x30, 0x28, 0xc7, 0x27, 0x1a, 0x4b, 0xc, 0x98, 0xd8, 0x4f, 0x2f, 0x34, 0xed, 0xb7, 0x13, 0x59, 0x7a, 0x98, 0x17, 0x34, 0x1f, 0xb7, 0x52, 0x52, 0xf1, 0x7a, 0x5d, 0xde, 0xd2, 0xb2, 0x9e, 0x78, 0x45, 0xb6, 0x61, 0xe7, 0xba, 0xb9, 0x48, 0xcd, 0xf5, 0xb6, 0xd, 0xc3, 0xe9, 0xe9, 0xd7, 0xf4, 0xbf, 0xe7, 0x7e, 0xdf, 0xff, 0x0, 0xda, 0x19, 0x15, 0x34, 0xd5, 0xa4, 0x90, 0x50, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xa2, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3b, 0x3b, 0x43, 0x42, 0x42, 0x4b, 0x3e, 0x3e, 0x47, 0x3e, 0x3e, 0x46, 0x41, 0x41, 0x4a, 0x0, 0x0, 0x0, 0x3d, 0x3d, 0x45, 0x3b, 0x3b, 0x43, 0x3a, 0x3a, 0x42, 0x38, 0x38, 0x41, 0x37, 0x37, 0x3e, 0x36, 0x36, 0x3d, 0x35, 0x35, 0x3c, 0x0, 0x0, 0x0, 0x38, 0x38, 0x40, 0x38, 0x38, 0x40, 0x31, 0x31, 0x38, 0x34, 0x34, 0x3b, 0x34, 0x34, 0x3b, 0x39, 0x39, 0x3f, 0x31, 0x31, 0x38, 0x2f, 0x2f, 0x36, 0x2d, 0x2d, 0x33, 0x2c, 0x2c, 0x32, 0x2b, 0x2b, 0x31, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x29, 0x29, 0x30, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x28, 0x28, 0x2d, 0x27, 0x27, 0x2d, 0x27, 0x27, 0x2c, 0x29, 0x29, 0x2e, 0x26, 0x26, 0x2c, 0x36, 0xc6, 0xc8, 0x93, 0x0, 0x0, 0x0, 0x28, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0x4, 0x13, 0x19, 0x1f, 0x22, 0x23, 0x16, 0x27, 0x35, 0x3f, 0x45, 0x46, 0x94, 0xf5, 0xfa, 0xfb, 0xf5, 0x40, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0x1a, 0xf5, 0xf6, 0x95, 0xfa, 0xfb, 0xf4, 0x94, 0x71, 0xda, 0xac, 0x92, 0x0, 0x0, 0x0, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0x8f, 0x35, 0x82, 0xc3, 0x0, 0xc, 0x4, 0x77, 0x24, 0x85, 0xba, 0xe3, 0xff, 0xff, 0xee, 0xca, 0x74, 0x41, 0xdb, 0x32, 0xf3, 0x94, 0x82, 0x85, 0x10, 0x1d, 0x92, 0xb2, 0x3, 0x8e, 0x95, 0x77, 0x93, 0x6c, 0x28, 0xed, 0x15, 0x54, 0x67, 0xa6, 0x41, 0x3e, 0x8, 0x9c, 0xc3, 0xf4, 0xf2, 0xf6, 0x2a, 0x80, 0xf8, 0x44, 0x2d, 0x79, 0x2d, 0x20, 0xe0, 0x2, 0xa8, 0xc3, 0x2e, 0x6f, 0xc, 0x9e, 0x4c, 0x3c, 0x21, 0x4, 0xd8, 0xf0, 0x2, 0x28, 0x24, 0xcd, 0x3, 0xa9, 0x19, 0x64, 0xce, 0x83, 0x4c, 0x45, 0xe6, 0x69, 0x1a, 0xd8, 0xe9, 0x99, 0x96, 0x7f, 0x77, 0x37, 0x59, 0x83, 0xcc, 0xef, 0x7f, 0x89, 0x1f, 0x8e, 0xbf, 0x95, 0xd3, 0x1d, 0xf0, 0xff, 0x7a, 0x63, 0x7e, 0x86, 0xcb, 0x73, 0x8c, 0x5e, 0xee, 0xca, 0xb1, 0xad, 0x5f, 0x3, 0xaf, 0xdb, 0x49, 0x94, 0x4b, 0x90, 0x40, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_bg_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x7b, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x67, 0x7a, 0x85, 0x66, 0x7a, 0x86, 0x68, 0x7b, 0x86, 0x57, 0x51, 0x51, 0x4c, 0x42, 0x40, 0x4d, 0x43, 0x41, 0x56, 0x4c, 0x4b, 0x4d, 0x44, 0x41, 0x4e, 0x44, 0x42, 0x4f, 0x45, 0x43, 0x67, 0x7b, 0x87, 0x4f, 0x44, 0x43, 0x50, 0x45, 0x44, 0x52, 0x46, 0x44, 0x51, 0x46, 0x45, 0x4b, 0x40, 0x3f, 0x51, 0x47, 0x45, 0x52, 0x48, 0x46, 0x53, 0x48, 0x47, 0x4b, 0x41, 0x3f, 0x54, 0x49, 0x46, 0x55, 0x4a, 0x47, 0x55, 0x49, 0x47, 0x68, 0x7c, 0x88, 0x4a, 0x40, 0x3e, 0x55, 0x4b, 0x49, 0x56, 0x4d, 0x4b, 0x53, 0x49, 0x47, 0x50, 0x46, 0x44, 0x4a, 0x41, 0x3e, 0x48, 0x3e, 0x3c, 0x4b, 0x42, 0x3f, 0x49, 0x3f, 0x3d, 0x46, 0x3d, 0x3c, 0x47, 0x3d, 0x3b, 0x47, 0x3e, 0x3b, 0x49, 0x40, 0x3d, 0x45, 0x3c, 0x3b, 0x46, 0x3c, 0x3a, 0xff, 0xff, 0xff, 0x2e, 0x48, 0xbd, 0x3e, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x28, 0xbd, 0xb0, 0xb5, 0xb2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xb3, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x2d, 0x8f, 0xdb, 0x16, 0x82, 0x20, 0x10, 0x45, 0x11, 0x19, 0x12, 0x91, 0x54, 0x44, 0x93, 0xb2, 0x48, 0xa1, 0xfc, 0xff, 0x3f, 0xec, 0xa0, 0xed, 0xb7, 0xd9, 0x73, 0xd6, 0x5c, 0x18, 0x3, 0x5, 0x2f, 0x1, 0x2f, 0xd8, 0x49, 0xc1, 0x5, 0x49, 0x40, 0xe2, 0x54, 0xc5, 0x85, 0x2a, 0xa9, 0x80, 0xac, 0x48, 0x64, 0xc3, 0x89, 0x64, 0x7d, 0x20, 0x89, 0x34, 0x2, 0x82, 0x48, 0x35, 0xe6, 0x7a, 0x6d, 0x1b, 0x45, 0x39, 0xc2, 0x3b, 0x92, 0x4d, 0x6f, 0x87, 0xc1, 0xf6, 0x30, 0x4e, 0x33, 0xed, 0xb2, 0x18, 0xa7, 0xdb, 0x64, 0x8d, 0x24, 0x37, 0x33, 0xed, 0x9d, 0xac, 0xfb, 0xf1, 0xfe, 0x18, 0x97, 0xa7, 0x3a, 0xc4, 0xcb, 0x51, 0x16, 0xf7, 0xc9, 0x42, 0x78, 0x88, 0xf0, 0x76, 0xca, 0xb4, 0xcb, 0x62, 0xd, 0x2, 0x2b, 0xc4, 0x16, 0x7c, 0x5e, 0x63, 0x9e, 0x35, 0x75, 0x6b, 0xd4, 0x58, 0x9b, 0x3e, 0x98, 0x8b, 0xbb, 0xd0, 0x8f, 0xa2, 0x64, 0x88, 0xc4, 0xb0, 0x7a, 0xe7, 0x3a, 0x1f, 0x12, 0x2, 0xf9, 0xb2, 0x6f, 0x8c, 0x31, 0x84, 0x18, 0xd3, 0x26, 0xf8, 0xf1, 0x8c, 0x16, 0xfb, 0x9e, 0xd2, 0xbe, 0xb, 0x5d, 0xfe, 0xff, 0xe5, 0x7a, 0x6, 0x67, 0xf9, 0x3, 0x92, 0x36, 0xd, 0x2d, 0xc1, 0xf2, 0x6d, 0x3c, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x67, 0x7a, 0x85, 0x66, 0x7a, 0x86, 0x68, 0x7b, 0x86, 0x57, 0x51, 0x51, 0x4c, 0x42, 0x40, 0x4d, 0x43, 0x41, 0x56, 0x4c, 0x4b, 0x4d, 0x44, 0x41, 0x4e, 0x44, 0x42, 0x4f, 0x45, 0x43, 0x67, 0x7b, 0x87, 0x4f, 0x44, 0x43, 0x50, 0x45, 0x44, 0x52, 0x46, 0x44, 0x51, 0x46, 0x45, 0x4b, 0x40, 0x3f, 0x51, 0x47, 0x45, 0x52, 0x48, 0x46, 0x53, 0x48, 0x47, 0x4b, 0x41, 0x3f, 0x54, 0x49, 0x46, 0x55, 0x4a, 0x47, 0x55, 0x49, 0x47, 0x68, 0x7c, 0x88, 0x4a, 0x40, 0x3e, 0x55, 0x4b, 0x49, 0x56, 0x4d, 0x4b, 0x53, 0x49, 0x47, 0x50, 0x46, 0x44, 0x4a, 0x41, 0x3e, 0x48, 0x3e, 0x3c, 0x4b, 0x42, 0x3f, 0x49, 0x3f, 0x3d, 0x46, 0x3d, 0x3c, 0x47, 0x3d, 0x3b, 0x47, 0x3e, 0x3b, 0x49, 0x40, 0x3d, 0x45, 0x3c, 0x3b, 0x46, 0x3c, 0x3a, 0xdd, 0x63, 0x56, 0x8d, 0x0, 0x0, 0x0, 0xad, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2d, 0x88, 0x35, 0x42, 0x4, 0x0, 0xc, 0x4, 0x77, 0x93, 0x9c, 0xbb, 0xf4, 0xc8, 0xff, 0xdf, 0x84, 0xb5, 0xb8, 0x5b, 0x84, 0x0, 0x37, 0xc5, 0xca, 0x10, 0xd, 0xc9, 0xce, 0xaa, 0xea, 0x24, 0x40, 0xca, 0x41, 0x64, 0x2b, 0x5, 0x87, 0x34, 0xa8, 0x88, 0x54, 0xef, 0x82, 0x80, 0x89, 0x34, 0x36, 0x96, 0xe0, 0x14, 0xa4, 0x12, 0xa6, 0x24, 0x18, 0xe1, 0xa8, 0x50, 0x59, 0x7c, 0x73, 0x30, 0x50, 0x55, 0x4a, 0x31, 0xd7, 0xb4, 0x89, 0xa1, 0x51, 0xb2, 0x9c, 0x1, 0x2c, 0x4, 0x83, 0x15, 0x12, 0x30, 0xab, 0xe9, 0x5a, 0x1, 0xb4, 0x40, 0xa1, 0x29, 0xbe, 0x75, 0xe, 0x5a, 0x70, 0xbe, 0x2a, 0xff, 0x12, 0xf1, 0xef, 0x1b, 0x5f, 0x8d, 0x5b, 0x68, 0xd, 0xdc, 0xe3, 0xf1, 0x71, 0x16, 0x3e, 0x5b, 0xc8, 0x33, 0xa9, 0xc7, 0xbc, 0x7f, 0xa4, 0x22, 0x6a, 0xb5, 0x90, 0xcb, 0xb2, 0x1a, 0x25, 0x67, 0x8b, 0x8f, 0x6f, 0xf8, 0x64, 0xa8, 0x35, 0x7a, 0x25, 0xa8, 0xa7, 0x1, 0x38, 0xc, 0xcc, 0xab, 0x4c, 0x5, 0xea, 0xe3, 0x76, 0x2f, 0x54, 0x93, 0xf3, 0xf, 0x4f, 0x10, 0x8d, 0x4c, 0x16, 0x9d, 0xcf, 0x1f, 0xd1, 0xf9, 0x3, 0x34, 0xc8, 0x4a, 0xb4, 0x1d, 0xb, 0xcd, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_checked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xf, 0xbe, 0x8b, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xa3, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x85, 0xcd, 0xa1, 0xa, 0xc2, 0x50, 0x0, 0x85, 0xe1, 0xff, 0xdc, 0x5d, 0xd8, 0x14, 0x4, 0xab, 0x69, 0x37, 0xac, 0x98, 0xd5, 0x27, 0xb0, 0x89, 0x16, 0x8b, 0xd5, 0xf7, 0xd0, 0xec, 0x73, 0xf8, 0x4, 0x16, 0x61, 0x4d, 0x10, 0xc, 0x16, 0xa3, 0x61, 0x71, 0xab, 0x16, 0xeb, 0x84, 0x3b, 0xae, 0x45, 0x8b, 0xa, 0x9e, 0x78, 0xce, 0x7, 0x7, 0xfe, 0x44, 0xbf, 0xca, 0xa2, 0x28, 0x3a, 0x71, 0x1c, 0x1f, 0x24, 0x3d, 0xcc, 0xe7, 0x18, 0x42, 0x50, 0x92, 0x24, 0x5b, 0x49, 0x23, 0xa0, 0xfd, 0x5, 0xaa, 0xaa, 0x5a, 0x1, 0x73, 0xe0, 0x1e, 0x45, 0xd1, 0x42, 0x65, 0x59, 0x9e, 0x80, 0x96, 0xf7, 0x7e, 0x62, 0xad, 0x1d, 0x2, 0xfb, 0x97, 0x9d, 0x39, 0xe7, 0x72, 0x1b, 0x42, 0x88, 0x25, 0xd, 0xac, 0xb5, 0x47, 0xa0, 0x7, 0x18, 0x60, 0xed, 0x9c, 0xcb, 0x1, 0x4c, 0xd3, 0x34, 0x53, 0xe0, 0xa, 0xf4, 0x81, 0x2e, 0xb0, 0x4b, 0xd3, 0x74, 0xf3, 0xbe, 0x34, 0x59, 0x96, 0xdd, 0xbc, 0xf7, 0x63, 0xe0, 0x2, 0x9c, 0xeb, 0xba, 0x5e, 0x4a, 0xa, 0x6f, 0xf0, 0x4, 0x57, 0x3d, 0x2c, 0x27, 0x2b, 0xe9, 0x62, 0x6b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x56, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x3, 0x6e, 0xf0, 0xde, 0x3f, 0xf5, 0xe0, 0x30, 0x9c, 0xfb, 0x9f, 0xf1, 0xc1, 0xda, 0x7, 0xff, 0x1f, 0x9c, 0x85, 0xb, 0x3c, 0xa8, 0x1, 0x72, 0xdf, 0x3d, 0x56, 0x61, 0x78, 0x70, 0xf8, 0xc1, 0x99, 0x3b, 0x62, 0xf, 0xbc, 0x1e, 0xfc, 0x5, 0x42, 0x2f, 0xa0, 0xcc, 0xfd, 0x53, 0x40, 0x99, 0x6b, 0xf, 0xde, 0x3, 0xc9, 0x6a, 0xb0, 0x52, 0xa0, 0xec, 0xe5, 0x7, 0xff, 0x81, 0x70, 0xed, 0x7f, 0x46, 0x20, 0x17, 0x2a, 0x74, 0xfa, 0xc1, 0xb1, 0x1b, 0xbc, 0x10, 0x1e, 0x0, 0xfd, 0x1f, 0x33, 0x9, 0xf7, 0x50, 0x16, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_hover_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x20, 0x2e, 0x31, 0x83, 0xae, 0xb7, 0xb3, 0xd8, 0xe1, 0xaf, 0xd5, 0xde, 0xac, 0xd2, 0xdb, 0xa9, 0xcf, 0xd8, 0xa5, 0xcc, 0xd5, 0xa2, 0xc9, 0xd2, 0x9e, 0xc6, 0xcf, 0x9b, 0xc3, 0xcc, 0x97, 0xc0, 0xc9, 0x94, 0xbd, 0xc6, 0x91, 0xba, 0xc3, 0x8d, 0xb7, 0xc0, 0xff, 0xff, 0xff, 0x73, 0xd4, 0x4e, 0xcb, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x14, 0x4, 0x3, 0x1, 0x6, 0x21, 0x25, 0x30, 0x50, 0x64, 0x10, 0x32, 0x6, 0x3, 0x20, 0xc3, 0x5, 0xc, 0x80, 0x8c, 0x50, 0x30, 0x0, 0x32, 0xd2, 0xc0, 0x0, 0xc8, 0x28, 0x7, 0x3, 0x20, 0xa3, 0x3, 0xc, 0x80, 0x8c, 0x99, 0x60, 0x0, 0x64, 0xac, 0x2, 0x3, 0x20, 0x63, 0x37, 0x18, 0x0, 0x19, 0x67, 0xc0, 0x0, 0xc8, 0xb8, 0xb, 0x6, 0x40, 0xc6, 0x3b, 0x30, 0x50, 0x44, 0x58, 0xa, 0x73, 0x6, 0x0, 0xe9, 0xb4, 0x2d, 0xf5, 0x51, 0xd4, 0xb8, 0xa1, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0x20, 0x2e, 0x31, 0x83, 0xae, 0xb7, 0xb3, 0xd8, 0xe1, 0xaf, 0xd5, 0xde, 0xac, 0xd2, 0xdb, 0xa9, 0xcf, 0xd8, 0xa5, 0xcc, 0xd5, 0xa2, 0xc9, 0xd2, 0x9e, 0xc6, 0xcf, 0x9b, 0xc3, 0xcc, 0x97, 0xc0, 0xc9, 0x94, 0xbd, 0xc6, 0x91, 0xba, 0xc3, 0x8d, 0xb7, 0xc0, 0x9c, 0x2c, 0x91, 0xa9, 0x0, 0x0, 0x0, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x62, 0x14, 0x4, 0x3, 0x1, 0x26, 0x41, 0x28, 0x60, 0x62, 0x80, 0xd0, 0xc, 0x74, 0x61, 0x98, 0x80, 0x1, 0x3, 0xd3, 0x7b, 0x28, 0x0, 0x0, 0x1a, 0x86, 0xe, 0x98, 0x2c, 0x61, 0xda, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_unchecked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x18, 0x32, 0xe0, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x40, 0x5, 0x0, 0x0, 0x10, 0x0, 0x1, 0xa1, 0xc5, 0x21, 0xc1, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x18, 0x32, 0xe0, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char popup_window_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x46, 0x8, 0x3, 0x0, 0x0, 0x0, 0x8d, 0x2b, 0xf6, 0x48, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x6e, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x46, 0x44, 0x4f, 0xff, 0xff, 0xff, 0x7e, 0xde, 0x1d, 0x81, 0x0, 0x0, 0x0, 0x33, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xa2, 0x3, 0x9, 0x17, 0xc, 0x20, 0xf, 0x2a, 0x5e, 0x12, 0x30, 0x68, 0x46, 0x20, 0x4e, 0xa2, 0x7d, 0x3a, 0x4f, 0xa4, 0x7d, 0x3f, 0x25, 0x60, 0xc0, 0xb8, 0x57, 0x1d, 0xba, 0x59, 0xbd, 0x5b, 0x22, 0xbf, 0x5e, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xa1, 0x9f, 0x9e, 0x52, 0x92, 0x15, 0x44, 0x7e, 0xd8, 0x5, 0xc7, 0xf4, 0xac, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x79, 0xa1, 0xdc, 0xd4, 0xd0, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x1, 0xe8, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0xd6, 0xd9, 0x53, 0xd3, 0x60, 0x14, 0xc6, 0xe1, 0x92, 0x5a, 0xa3, 0x44, 0xa3, 0xd6, 0x85, 0xa0, 0xb1, 0x5a, 0xcb, 0xda, 0xbd, 0x7c, 0xb8, 0xef, 0x2b, 0x69, 0x21, 0xd0, 0xda, 0x22, 0x25, 0xd, 0x69, 0x20, 0x60, 0x69, 0x11, 0x2a, 0x22, 0xcb, 0x9f, 0xef, 0x78, 0x97, 0x7e, 0xe7, 0x3d, 0x33, 0x1d, 0xbd, 0x70, 0xfc, 0x5d, 0x3f, 0x77, 0xe7, 0xe2, 0x3d, 0x91, 0xc8, 0x7f, 0xd8, 0x88, 0x60, 0x1a, 0x21, 0x54, 0xcc, 0x3f, 0x84, 0xcd, 0xb, 0x4a, 0x1f, 0x31, 0xfd, 0x1d, 0x7d, 0xcc, 0x4, 0xe8, 0x13, 0x26, 0x40, 0x9f, 0x32, 0x1, 0xfa, 0x8c, 0x9, 0xd0, 0xe7, 0x4c, 0x80, 0xbe, 0x60, 0x2, 0xf4, 0x25, 0x13, 0xa0, 0xaf, 0x98, 0x0, 0x7d, 0xcd, 0x44, 0xa8, 0x22, 0xde, 0x30, 0x49, 0x54, 0x89, 0x9e, 0x13, 0x6f, 0x99, 0x64, 0x1a, 0x3b, 0x2f, 0xde, 0x31, 0x49, 0x54, 0xbd, 0x70, 0x51, 0xbc, 0x67, 0x92, 0xe8, 0xa8, 0x76, 0x49, 0x7c, 0x60, 0x12, 0x97, 0xf5, 0x68, 0x88, 0xea, 0x57, 0xae, 0x8a, 0x8f, 0x4c, 0xe2, 0x5a, 0x7c, 0x34, 0x4c, 0xaf, 0xdf, 0x10, 0x9f, 0x98, 0xc4, 0xcd, 0x5b, 0x21, 0xaa, 0x8c, 0x19, 0xe3, 0xb, 0x56, 0x19, 0x66, 0x2d, 0xdc, 0xbe, 0x63, 0x2a, 0x43, 0xd2, 0xbb, 0x9, 0x7d, 0x58, 0x7a, 0xef, 0x7e, 0x72, 0x58, 0xfa, 0x20, 0x35, 0x36, 0x2c, 0x1d, 0x37, 0x8, 0xad, 0xc0, 0x0, 0x5d, 0x5c, 0xb2, 0x61, 0x4b, 0x8b, 0x32, 0x9d, 0x58, 0x5e, 0xa9, 0xc2, 0x56, 0x96, 0x27, 0x8, 0xad, 0x7d, 0x86, 0xd5, 0x28, 0xad, 0xd7, 0x1a, 0xb0, 0x5a, 0x9d, 0xd2, 0x55, 0x4c, 0x57, 0x1, 0xad, 0x7c, 0x81, 0x55, 0x8, 0x9d, 0x5c, 0xb3, 0x9b, 0x30, 0x7b, 0x6d, 0x52, 0xa2, 0x53, 0xeb, 0x55, 0x7, 0x56, 0x5d, 0x9f, 0x22, 0xb4, 0xe5, 0xc2, 0x5a, 0x94, 0x6e, 0x34, 0x30, 0x6d, 0x6c, 0x10, 0xea, 0xb5, 0x7d, 0x58, 0xdb, 0x93, 0xe9, 0xf4, 0x66, 0x13, 0xd3, 0xe6, 0xe6, 0xb4, 0x4c, 0xb7, 0x9c, 0x0, 0xe6, 0x6c, 0x11, 0xba, 0xed, 0x62, 0xea, 0x6e, 0x13, 0xba, 0xe3, 0x7e, 0x85, 0xb9, 0x3b, 0x84, 0x96, 0xfd, 0xe, 0xcc, 0x2f, 0x13, 0xba, 0xeb, 0x77, 0x61, 0xfe, 0x2e, 0xa1, 0x76, 0xd0, 0x83, 0x5, 0x36, 0xa1, 0x7b, 0xc1, 0x37, 0x58, 0xb0, 0x27, 0xd3, 0x99, 0xfd, 0x83, 0x3e, 0xec, 0x60, 0x7f, 0x46, 0xa2, 0xb3, 0xad, 0xce, 0x77, 0x58, 0xa7, 0x35, 0x2b, 0xd3, 0xf6, 0x21, 0xa6, 0x87, 0x6d, 0x40, 0x7f, 0xc0, 0x0, 0x3d, 0xea, 0xfd, 0x84, 0xf5, 0x8e, 0x8, 0x75, 0xfa, 0x98, 0xf6, 0x1d, 0x42, 0x8f, 0x39, 0x7a, 0x4c, 0xe9, 0xc9, 0x29, 0xec, 0x64, 0x90, 0x46, 0x92, 0xa9, 0xb4, 0x75, 0xca, 0x64, 0xa5, 0x53, 0xc9, 0xc8, 0x1f, 0xd2, 0xc, 0x4f, 0x33, 0x3, 0x54, 0x4f, 0x64, 0x79, 0x9a, 0x4d, 0xe8, 0x21, 0x6a, 0xe6, 0xf2, 0x1e, 0x47, 0xbd, 0x7c, 0xce, 0xc, 0xd1, 0x42, 0xb1, 0x54, 0xef, 0x62, 0xd9, 0x3d, 0x2b, 0x15, 0xb, 0x21, 0xaa, 0x6a, 0xc6, 0xdc, 0x99, 0x67, 0x81, 0xbc, 0xfa, 0x9c, 0xa1, 0xa9, 0x21, 0x1a, 0x35, 0xe3, 0x46, 0x29, 0x9f, 0xcd, 0xa4, 0xa5, 0x32, 0xd9, 0x7c, 0xc9, 0x88, 0x9b, 0xe1, 0xe1, 0x54, 0x62, 0xa6, 0x56, 0xcc, 0x25, 0x52, 0xa4, 0x44, 0xae, 0xa8, 0x99, 0xb1, 0xd0, 0x5, 0x7e, 0x3f, 0x4, 0x6a, 0xc1, 0xd4, 0x93, 0x24, 0xdd, 0x2c, 0xa8, 0xd1, 0x1, 0xf9, 0xf, 0xfb, 0x5, 0x66, 0x6f, 0x2a, 0x9a, 0xa8, 0x51, 0x81, 0xce, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x46, 0x8, 0x3, 0x0, 0x0, 0x0, 0x8d, 0x2b, 0xf6, 0x48, 0x0, 0x0, 0x1, 0x6b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xe8, 0xe5, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x22, 0x0, 0x0, 0x0, 0x1a, 0x19, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x1d, 0x21, 0x17, 0x16, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1b, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x1e, 0x1c, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x20, 0x1e, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x21, 0x1f, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x20, 0x25, 0x0, 0x0, 0x0, 0x20, 0x20, 0x25, 0x20, 0x1d, 0x25, 0x20, 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1d, 0x1d, 0x20, 0x1d, 0x1a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x30, 0x38, 0xe8, 0xe5, 0xf1, 0xe5, 0xe2, 0xeb, 0xe3, 0xe1, 0xe8, 0xe1, 0xdf, 0xe7, 0xe0, 0xde, 0xe6, 0xdf, 0xdd, 0xe5, 0xde, 0xdc, 0xe4, 0xdd, 0xdb, 0xe3, 0xdc, 0xda, 0xe2, 0xda, 0xd8, 0xe0, 0xd9, 0xd7, 0xdf, 0xd7, 0xd6, 0xdf, 0xd6, 0xd4, 0xdd, 0xd5, 0xd3, 0xdc, 0xd4, 0xd1, 0xdb, 0xd3, 0xd0, 0xda, 0xd1, 0xce, 0xd8, 0xd0, 0xcd, 0xd7, 0xcf, 0xcd, 0xd7, 0xe2, 0xdf, 0xeb, 0x48, 0x46, 0x51, 0x42, 0x40, 0x4b, 0x40, 0x3e, 0x48, 0x40, 0x3d, 0x48, 0x48, 0x45, 0x50, 0x42, 0x3f, 0x4a, 0x3f, 0x3d, 0x48, 0x47, 0x44, 0x50, 0x41, 0x3f, 0x4a, 0x3f, 0x3d, 0x47, 0x41, 0x3e, 0x49, 0x3f, 0x3c, 0x47, 0x46, 0x43, 0x4f, 0x3e, 0x3c, 0x46, 0x40, 0x3e, 0x49, 0x3d, 0x3b, 0x46, 0x45, 0x43, 0x4e, 0x3d, 0x3b, 0x45, 0x44, 0x42, 0x4d, 0x3d, 0x3a, 0x45, 0x3e, 0x3c, 0x47, 0x3c, 0x3a, 0x44, 0x43, 0x42, 0x4c, 0x43, 0x40, 0x4c, 0x3e, 0x3b, 0x46, 0x3b, 0x39, 0x43, 0x43, 0x3f, 0x4c, 0x43, 0x3f, 0x4b, 0x3a, 0x38, 0x42, 0x42, 0x3e, 0x4b, 0x42, 0x3e, 0x49, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3f, 0x3e, 0x48, 0x39, 0x37, 0x40, 0x38, 0x36, 0x40, 0x3e, 0x3d, 0x48, 0x38, 0x36, 0x3f, 0x3e, 0x3d, 0x47, 0x3a, 0x38, 0x41, 0x38, 0x35, 0x3f, 0x37, 0x35, 0x3e, 0x39, 0x36, 0x40, 0x37, 0x34, 0x3e, 0x3d, 0x3a, 0x46, 0x36, 0x34, 0x3d, 0x3d, 0x3a, 0x44, 0x37, 0x35, 0x3f, 0x35, 0x33, 0x3c, 0x46, 0x44, 0x4f, 0xac, 0xa5, 0x1, 0x25, 0x0, 0x0, 0x0, 0x33, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xa2, 0x3, 0x9, 0x17, 0xc, 0x20, 0xf, 0x2a, 0x5e, 0x12, 0x30, 0x68, 0x46, 0x20, 0x4e, 0xa2, 0x7d, 0x3a, 0x4f, 0xa4, 0x7d, 0x3f, 0x25, 0x60, 0xc0, 0xb8, 0x57, 0x1d, 0xba, 0x59, 0xbd, 0x5b, 0x22, 0xbf, 0x5e, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xa1, 0x9f, 0x9e, 0x52, 0x92, 0x15, 0x44, 0x7e, 0xd8, 0x5, 0xc7, 0xf4, 0xac, 0x0, 0x0, 0x1, 0x98, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xd6, 0x55, 0x9e, 0x14, 0x31, 0x10, 0x80, 0xf1, 0xb2, 0x20, 0x1d, 0xdc, 0x9d, 0x3b, 0x2c, 0xa7, 0x87, 0x4b, 0xe0, 0xee, 0xd0, 0x82, 0xcb, 0xea, 0xb4, 0x86, 0x79, 0x23, 0x93, 0xaa, 0xcc, 0xf, 0xd7, 0xfd, 0x9e, 0xff, 0xed, 0x1d, 0x21, 0xf8, 0xe2, 0xfe, 0x1c, 0x8a, 0x17, 0x32, 0xa1, 0xa2, 0x2b, 0x48, 0x66, 0xb8, 0xa2, 0x28, 0x10, 0x9a, 0xd1, 0xf7, 0x3d, 0x16, 0x66, 0xfa, 0x45, 0x74, 0x9b, 0xd2, 0x97, 0x52, 0xe2, 0x2f, 0xa6, 0x40, 0x5f, 0x4c, 0x1d, 0xf3, 0x97, 0x52, 0x5e, 0x46, 0xf7, 0xee, 0xe3, 0x88, 0x8a, 0x48, 0x9e, 0x8a, 0xec, 0x8c, 0x28, 0x2c, 0xa7, 0xf0, 0x99, 0xd2, 0x6e, 0xe7, 0x8e, 0x10, 0x9b, 0xd1, 0x11, 0xe7, 0xe, 0xd1, 0x17, 0x8e, 0x2, 0xe6, 0x55, 0xf8, 0x42, 0x4a, 0x74, 0x18, 0xbe, 0xf8, 0xac, 0x9b, 0x5f, 0x4a, 0xb7, 0x36, 0x20, 0xa5, 0xf9, 0x2f, 0x90, 0x50, 0x11, 0x36, 0x13, 0x49, 0xa9, 0xe7, 0x6c, 0x5e, 0xcf, 0x2e, 0x99, 0xf4, 0xd, 0xb8, 0x8c, 0x5, 0xa7, 0x28, 0x8, 0x98, 0x9, 0x68, 0xba, 0x41, 0x66, 0x1b, 0x8a, 0xa2, 0xb0, 0x4d, 0x59, 0x30, 0xa5, 0x94, 0xa3, 0x94, 0x52, 0x0, 0x6f, 0x53, 0x6f, 0x7c, 0x82, 0x16, 0xcc, 0x5a, 0x51, 0x14, 0xbd, 0x98, 0x79, 0x4c, 0x29, 0x72, 0xee, 0xac, 0x8c, 0xea, 0xac, 0xb9, 0x51, 0xa0, 0xce, 0xa, 0x44, 0x60, 0x46, 0xa4, 0x28, 0x2, 0x9b, 0x1, 0x2a, 0x5a, 0xa, 0x9a, 0x49, 0xa9, 0xe8, 0x79, 0x20, 0x33, 0x38, 0xaf, 0x68, 0xc5, 0x68, 0xc6, 0x95, 0xa2, 0xe7, 0x72, 0x67, 0x3d, 0x97, 0x52, 0x24, 0xcf, 0x66, 0x9e, 0x30, 0xa5, 0xef, 0x5a, 0x34, 0x6b, 0xdf, 0xa5, 0x14, 0xa4, 0x0, 0xb3, 0x42, 0x8c, 0xe5, 0x38, 0x37, 0xb4, 0x14, 0x3d, 0xd2, 0xda, 0xb4, 0x3d, 0xa2, 0x68, 0xe3, 0xc0, 0xcc, 0x35, 0x8a, 0x9e, 0x86, 0x4c, 0xa7, 0x15, 0x85, 0x9d, 0x6c, 0xb6, 0x13, 0x16, 0xe8, 0x54, 0x78, 0xbc, 0x8e, 0x60, 0x86, 0xd7, 0xd1, 0x17, 0xd3, 0x37, 0x6e, 0x48, 0x30, 0x4f, 0x70, 0x81, 0xbe, 0xed, 0x97, 0xd1, 0xfe, 0x6d, 0x44, 0xf7, 0xed, 0xa7, 0x63, 0x39, 0x79, 0x8c, 0xf6, 0xef, 0x8b, 0xe8, 0x61, 0xe6, 0x8d, 0x55, 0x5b, 0xae, 0x9e, 0x62, 0x3e, 0x1c, 0xd1, 0x3b, 0xf7, 0x9b, 0xc7, 0xfb, 0x9b, 0xab, 0x46, 0xcd, 0xab, 0x4b, 0xcd, 0xfd, 0x3b, 0x11, 0x1d, 0xe, 0x76, 0xf5, 0xc5, 0x2b, 0xe5, 0xf3, 0x67, 0x49, 0xcf, 0xcb, 0x2b, 0x8f, 0xea, 0xee, 0xe0, 0x10, 0xd1, 0xf0, 0xb2, 0x58, 0xb, 0x61, 0x75, 0xd6, 0x26, 0xcd, 0x56, 0x43, 0x58, 0x2b, 0x5e, 0x86, 0x88, 0x4e, 0x63, 0x33, 0x84, 0xf7, 0x1f, 0x26, 0xd5, 0x87, 0xf7, 0x61, 0x68, 0xc6, 0x29, 0xa2, 0x73, 0xdb, 0x5e, 0x2d, 0x57, 0x1f, 0xab, 0x56, 0xcb, 0xab, 0xed, 0x5c, 0xfe, 0xc4, 0x5d, 0xf1, 0x27, 0x1a, 0x8f, 0xba, 0x8d, 0xd7, 0xa0, 0x9a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char progress_bar_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0x27, 0x27, 0x27, 0xe1, 0x1d, 0x66, 0x4d, 0x0, 0x0, 0x0, 0xc, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xb7, 0x4a, 0xbe, 0x33, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x10, 0x95, 0xb2, 0xd, 0x2c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x44, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0xc5, 0xcf, 0x31, 0x16, 0x0, 0x10, 0xc, 0x44, 0xc1, 0x4d, 0x84, 0x4, 0xc1, 0xfd, 0x6f, 0xab, 0xc9, 0x53, 0x70, 0x0, 0x53, 0x6e, 0xb5, 0x1f, 0x20, 0x4e, 0x12, 0x12, 0x13, 0x40, 0xb9, 0xa8, 0x5, 0x2d, 0x99, 0xc0, 0xb5, 0x75, 0xf, 0xbd, 0x55, 0x86, 0xe8, 0x98, 0x2b, 0xcc, 0xa1, 0x2, 0x31, 0x5f, 0x87, 0xdb, 0xbf, 0xe1, 0x3e, 0xf6, 0x5c, 0x7f, 0xe2, 0xee, 0xfc, 0xd, 0x60, 0x3b, 0xa, 0x1d, 0x9e, 0x6a, 0x29, 0x33, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0x43, 0x65, 0x7d, 0x95, 0x0, 0x0, 0x0, 0xc, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xb7, 0x4a, 0xbe, 0x33, 0x0, 0x0, 0x0, 0x35, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x9f, 0xe, 0x66, 0xb4, 0x33, 0x7a, 0xb4, 0x1b, 0x0, 0x19, 0x7f, 0x3b, 0x28, 0x64, 0xc0, 0xd, 0x84, 0x5b, 0x1, 0xb7, 0x14, 0xee, 0xc, 0x0, 0xcf, 0x9d, 0x26, 0xff, 0xba, 0xcb, 0x90, 0x39, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char progress_fill_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x26, 0x78, 0x80, 0xa6, 0xcf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x60, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0x18, 0x78, 0xc0, 0x8, 0x63, 0x38, 0x58, 0x7c, 0xde, 0xf8, 0x45, 0xc, 0xc2, 0xe6, 0x79, 0xc5, 0xeb, 0x7f, 0xe0, 0x4, 0x9a, 0x2, 0xe3, 0x97, 0x1f, 0xb9, 0xff, 0x70, 0x43, 0xd8, 0x2c, 0x5f, 0xf9, 0xbf, 0x9e, 0x15, 0x87, 0xb2, 0x61, 0xa, 0xbe, 0x88, 0xfd, 0x81, 0x1b, 0xfb, 0x87, 0xfb, 0xb, 0x37, 0x8c, 0xcd, 0x44, 0xc8, 0xd, 0x54, 0x54, 0xc0, 0xf3, 0x8a, 0xe5, 0x2b, 0x8c, 0xcd, 0xf2, 0x95, 0xe7, 0x15, 0x86, 0x2, 0x5e, 0x7f, 0xfe, 0xaf, 0xec, 0xc, 0x10, 0xc8, 0xff, 0x95, 0xd7, 0x9f, 0xe6, 0x1, 0x4c, 0x2, 0x0, 0x0, 0x68, 0x3f, 0x16, 0xd7, 0xea, 0x7c, 0xdd, 0x1a, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0x4c, 0xc0, 0xc1, 0xc2, 0xf8, 0xa5, 0xfa, 0x7f, 0x8, 0x34, 0x7e, 0xe9, 0x60, 0x81, 0xa1, 0xc0, 0xf8, 0xa5, 0xca, 0x17, 0x85, 0xff, 0x10, 0xa8, 0xf2, 0xc5, 0xf8, 0x25, 0x86, 0x2, 0x75, 0xa0, 0x4, 0x1c, 0x2, 0x4d, 0xa1, 0xbf, 0x2, 0x4c, 0x47, 0x12, 0xf6, 0xe6, 0x20, 0x2, 0x0, 0x78, 0x21, 0x45, 0x61, 0x7f, 0xe2, 0xad, 0xaf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char radio_checked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xa, 0x69, 0x4, 0xd4, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x16, 0x7c, 0xd1, 0xa8, 0x19, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x69, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x6d, 0x8f, 0xdb, 0xe, 0xc0, 0x20, 0x8, 0x43, 0xdd, 0xc5, 0x3b, 0x9d, 0xa8, 0xec, 0xff, 0x7f, 0x75, 0x6a, 0x96, 0x2c, 0x8b, 0xf6, 0xad, 0x27, 0x14, 0x8a, 0x52, 0x2b, 0x69, 0x63, 0x9d, 0xb3, 0x46, 0xbf, 0x76, 0xf3, 0x21, 0x12, 0x40, 0x31, 0xf8, 0x7d, 0x0, 0x7f, 0x35, 0xdb, 0x45, 0x97, 0x1f, 0xf3, 0x81, 0x90, 0x38, 0x67, 0x4e, 0xa0, 0xd0, 0x53, 0x26, 0x22, 0x95, 0x2a, 0x52, 0x4b, 0x42, 0x34, 0xd, 0x58, 0x2, 0xd7, 0xbb, 0xa9, 0x32, 0xc8, 0x36, 0xe0, 0x80, 0x2c, 0x1d, 0x48, 0x6, 0xdc, 0xa, 0x4c, 0x91, 0x69, 0xe9, 0x74, 0x76, 0x2a, 0xa6, 0x8e, 0xaf, 0xfa, 0xb9, 0x7e, 0xee, 0xaf, 0x7, 0xb9, 0xfb, 0x8, 0xe7, 0x90, 0x1c, 0x65, 0x49, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x42, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xd3, 0xd3, 0xd3, 0xa2, 0xa2, 0xa2, 0x79, 0x79, 0x79, 0x73, 0x73, 0x73, 0x1c, 0x1c, 0x1c, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0xd1, 0xa7, 0xf5, 0xaa, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x63, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6d, 0x4f, 0x55, 0x2, 0x43, 0x31, 0x8, 0x4b, 0xe8, 0x6a, 0xf7, 0xbf, 0xeb, 0xc, 0x9b, 0x6b, 0x1f, 0x9f, 0xc4, 0x89, 0xbf, 0xbb, 0x3f, 0x2a, 0x49, 0x64, 0xa6, 0x3e, 0x1e, 0x1c, 0x7c, 0x3e, 0xf2, 0x14, 0xb7, 0xc7, 0xac, 0xf1, 0x10, 0xa6, 0xe8, 0x1, 0x44, 0xad, 0x42, 0xb9, 0x33, 0x22, 0x43, 0x95, 0x68, 0x55, 0xa4, 0xdc, 0x1f, 0x1e, 0xa1, 0x67, 0xa2, 0x57, 0x96, 0x22, 0x0, 0xc2, 0x3d, 0xf5, 0x44, 0x8c, 0x8a, 0x5d, 0x21, 0x80, 0x74, 0x83, 0x1e, 0x97, 0xc7, 0x22, 0x59, 0x4c, 0xd7, 0xd8, 0xb5, 0x18, 0x4a, 0x7b, 0x57, 0x57, 0xdb, 0x1a, 0xf7, 0x77, 0x17, 0x3a, 0x56, 0x4e, 0x11, 0x6f, 0x82, 0x20, 0xde, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char radio_unchecked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0xff, 0xff, 0xff, 0xbd, 0x7d, 0x89, 0x66, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xe, 0x6f, 0xbd, 0x30, 0x4f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x4a, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x40, 0x80, 0xb0, 0x8a, 0xf6, 0x54, 0x10, 0x2d, 0xb9, 0xfa, 0xcc, 0x99, 0x5d, 0x93, 0x80, 0x8c, 0xb9, 0x67, 0x80, 0xe0, 0x26, 0x3, 0x3, 0xeb, 0x1a, 0x10, 0xe3, 0x54, 0x0, 0x3, 0xdb, 0x1e, 0x10, 0xe3, 0x74, 0x2, 0x3, 0xfb, 0x19, 0x30, 0x28, 0x60, 0xe0, 0x80, 0x30, 0x1a, 0x10, 0xc, 0xb8, 0x14, 0x5c, 0x31, 0x5c, 0x3b, 0xdc, 0x40, 0x6, 0x4b, 0x90, 0x15, 0x53, 0x90, 0x2d, 0x85, 0x2, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xc4, 0xfc, 0x38, 0x7b, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4, 0x3, 0x4, 0x9, 0x9, 0x9, 0x6, 0x6, 0x6, 0xa, 0xa, 0xb, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x7f, 0x7f, 0x82, 0xd9, 0xd9, 0xd9, 0x47, 0x47, 0x48, 0x2b, 0x6e, 0xf2, 0xbf, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x88, 0xd1, 0xf7, 0x64, 0xf6, 0x2, 0xb3, 0xed, 0xd7, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x2, 0x61, 0x15, 0xed, 0xa9, 0x20, 0x5a, 0x72, 0xf5, 0x99, 0x33, 0xbb, 0x26, 0x1, 0x19, 0x73, 0xcf, 0x0, 0xc1, 0x4d, 0x6, 0x6, 0xd6, 0x35, 0x20, 0xc6, 0xa9, 0x0, 0x6, 0xb6, 0x3d, 0x20, 0xc6, 0xe9, 0x4, 0x6, 0xf6, 0x33, 0x60, 0x50, 0xc0, 0xc0, 0x1, 0x61, 0x34, 0xc0, 0x19, 0x70, 0x29, 0xb8, 0x62, 0xb8, 0x76, 0x84, 0x81, 0xc, 0x96, 0x20, 0x2b, 0xa6, 0xc0, 0x2d, 0x45, 0x0, 0x0, 0x37, 0xca, 0x3d, 0x81, 0xb4, 0x84, 0xb6, 0x80, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char reference_border_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xf, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xd1, 0xd1, 0xff, 0xb7, 0xb7, 0xff, 0x41, 0x41, 0xff, 0xff, 0xff, 0xd5, 0xfa, 0x24, 0x40, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x4, 0x8f, 0x68, 0xd9, 0x51, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x27, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x50, 0x32, 0x6, 0x3, 0x25, 0x6, 0x5, 0x6, 0x30, 0x60, 0x62, 0x30, 0x10, 0x4, 0x3, 0x66, 0x6, 0x3, 0x1, 0x90, 0x0, 0x23, 0x2d, 0x18, 0x30, 0x2b, 0xe0, 0x96, 0xc2, 0x9c, 0x1, 0x0, 0x5, 0x29, 0x7, 0xb, 0xf6, 0x43, 0xc2, 0xd4, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x2, 0x3, 0x0, 0x0, 0x0, 0x62, 0x9d, 0x17, 0xf2, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xd1, 0xd1, 0xff, 0xb7, 0xb7, 0xff, 0x41, 0x41, 0x2b, 0x2, 0x77, 0xea, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x25, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x58, 0xff, 0xff, 0xff, 0x2f, 0x86, 0x6, 0x6, 0x6, 0x26, 0x86, 0xa3, 0xa1, 0xa1, 0xc1, 0xc, 0x47, 0x18, 0x18, 0x84, 0x49, 0x22, 0xc0, 0xda, 0xc0, 0x6, 0x80, 0x8d, 0x2, 0x0, 0x36, 0x2b, 0x14, 0x3d, 0x85, 0x39, 0x85, 0x31, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x48, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xff, 0xff, 0xff, 0x34, 0x3f, 0xa6, 0x65, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x17, 0xb, 0xd6, 0x98, 0x8f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x5c, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0x8e, 0x49, 0xe, 0xc0, 0x20, 0xc, 0x3, 0x3, 0x61, 0x9, 0x3b, 0x61, 0xfb, 0xff, 0x53, 0x4b, 0x5b, 0x55, 0x15, 0x73, 0x1b, 0xc9, 0xb2, 0xd, 0x20, 0x24, 0x2a, 0xad, 0x15, 0x4a, 0x1, 0x20, 0x8c, 0x25, 0xc7, 0xec, 0xc8, 0x1a, 0x1, 0xd2, 0x87, 0xd6, 0xc7, 0xe8, 0x2d, 0x78, 0x9, 0x48, 0x6d, 0xae, 0xcd, 0x6c, 0x84, 0xa0, 0x62, 0x5f, 0xf, 0x3d, 0x2a, 0x48, 0x3c, 0x5e, 0x19, 0x9c, 0x4e, 0x39, 0x62, 0x47, 0x41, 0x2e, 0x5f, 0x75, 0xc9, 0x7b, 0xb4, 0x52, 0x64, 0x8e, 0x54, 0xcd, 0x7d, 0xe1, 0xbf, 0x73, 0x1, 0x30, 0x2f, 0x7, 0x53, 0x16, 0x34, 0xbd, 0xfa, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x45, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x40, 0x3e, 0x4a, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x20, 0x20, 0x24, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1e, 0x1e, 0x23, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x14, 0xee, 0x69, 0x20, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0x19, 0x40, 0x5d, 0x66, 0x28, 0x93, 0xf0, 0xfc, 0x94, 0xfc, 0xfd, 0x67, 0x1a, 0x96, 0x95, 0x1c, 0xf0, 0x43, 0x52, 0x0, 0x0, 0x0, 0x55, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0x8e, 0x45, 0x2, 0x80, 0x50, 0x10, 0x42, 0xc1, 0xee, 0xfb, 0x5f, 0xd4, 0xd6, 0xdf, 0xfd, 0x36, 0xd3, 0x3, 0x4, 0xd, 0x90, 0x6, 0xb2, 0x25, 0x39, 0xe0, 0xd2, 0xf9, 0xcb, 0x6a, 0x60, 0x6f, 0x27, 0xb7, 0xbc, 0x58, 0xb7, 0x53, 0x4d, 0x0, 0xf2, 0x3f, 0x5e, 0x36, 0x43, 0x5f, 0xc3, 0xf0, 0xdf, 0x17, 0xd7, 0xa6, 0xae, 0x60, 0x10, 0xff, 0x57, 0x16, 0xc5, 0x5a, 0xf1, 0x60, 0xe3, 0xe7, 0x5f, 0x37, 0x46, 0x74, 0xba, 0x9a, 0x16, 0xef, 0x37, 0x1c, 0x6f, 0x61, 0x47, 0x1, 0xa5, 0xc7, 0x32, 0x47, 0x38, 0x12, 0x92, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_down_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0x59, 0x59, 0x59, 0xb3, 0x52, 0xf2, 0x5, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xa, 0x68, 0xd0, 0xf4, 0x56, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xf1, 0x33, 0x66, 0x2, 0x1, 0x2a, 0xa3, 0x73, 0xe6, 0xcc, 0x19, 0x10, 0x35, 0x40, 0x1, 0x8, 0xa3, 0x73, 0x6, 0x1, 0x73, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0x5a, 0xfa, 0x3d, 0xf9, 0xfa, 0xe2, 0x64, 0xe2, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0x59, 0x59, 0x59, 0xb3, 0x52, 0xf2, 0x5, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xf1, 0x33, 0x66, 0x2, 0x1, 0x2a, 0xa3, 0x73, 0xe6, 0xcc, 0x19, 0x10, 0x35, 0x40, 0x1, 0x8, 0xa3, 0x73, 0x6, 0x1, 0x73, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0x5a, 0xfa, 0x3d, 0xf9, 0xfa, 0xe2, 0x64, 0xe2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_down_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xce, 0xce, 0xce, 0x59, 0x59, 0x59, 0xb8, 0xf5, 0x6d, 0x48, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xa, 0x68, 0xd0, 0xf4, 0x56, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xf1, 0x33, 0x66, 0x2, 0x1, 0x2a, 0xa3, 0x73, 0xe6, 0xcc, 0x19, 0x10, 0x35, 0x40, 0x1, 0x8, 0xa3, 0x73, 0x6, 0x1, 0x73, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0x5a, 0xfa, 0x3d, 0xf9, 0xfa, 0xe2, 0x64, 0xe2, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xce, 0xce, 0xce, 0x59, 0x59, 0x59, 0xb8, 0xf5, 0x6d, 0x48, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x33, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xf1, 0x33, 0x66, 0x2, 0x1, 0x2a, 0xa3, 0x73, 0xe6, 0xcc, 0x19, 0x10, 0x35, 0x40, 0x1, 0x8, 0xa3, 0x73, 0x6, 0x1, 0x73, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0x5a, 0xfa, 0x3d, 0xf9, 0xfa, 0xe2, 0x64, 0xe2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_left_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x2e, 0x28, 0xa3, 0x7b, 0xf7, 0x6e, 0x8, 0xa3, 0xe7, 0xcc, 0x19, 0xa8, 0x14, 0x9c, 0xd1, 0x7b, 0x17, 0xa6, 0xfd, 0x1d, 0x86, 0x81, 0x60, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0xcc, 0x4e, 0x3f, 0xd1, 0x4, 0x90, 0xbf, 0x60, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x3e, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x2e, 0x28, 0xa3, 0x7b, 0xf7, 0x6e, 0x8, 0xa3, 0xe7, 0xcc, 0x19, 0xa8, 0x14, 0x9c, 0xd1, 0x7b, 0x17, 0xa6, 0xfd, 0x1d, 0x86, 0x81, 0x60, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0xcc, 0x4e, 0x3f, 0xd1, 0x4, 0x90, 0xbf, 0x60, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_left_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0x59, 0x59, 0x59, 0x2a, 0x13, 0xff, 0x12, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x10, 0x95, 0xb2, 0xd, 0x2c, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x85, 0xcf, 0xcb, 0xe, 0x80, 0x20, 0xc, 0x44, 0xd1, 0xa2, 0x96, 0x47, 0x7, 0xe4, 0xff, 0xff, 0x16, 0xdc, 0x90, 0xe, 0xc6, 0x78, 0x97, 0x27, 0x69, 0xd3, 0x8a, 0x4, 0x75, 0x85, 0x43, 0x62, 0xca, 0xae, 0x14, 0x45, 0x33, 0xa5, 0xdf, 0x50, 0xa, 0x83, 0x99, 0x11, 0xa0, 0xa2, 0x7a, 0x0, 0xd0, 0xe0, 0xa1, 0x3d, 0xd1, 0xc8, 0x3d, 0xe3, 0xa5, 0xbd, 0x6f, 0x30, 0xe5, 0xef, 0xb0, 0x5, 0xaf, 0xe7, 0x4e, 0x7e, 0xff, 0x1a, 0xb, 0x26, 0x7, 0xac, 0xd9, 0xa3, 0x51, 0xe3, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0x58, 0xf9, 0x63, 0x6a, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x42, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x19, 0x33, 0xa1, 0x8c, 0xae, 0x55, 0x50, 0xc6, 0x9e, 0x3d, 0x10, 0x46, 0xf7, 0xee, 0xdb, 0x10, 0x46, 0xef, 0xdd, 0xbb, 0x50, 0xa9, 0x77, 0xef, 0xa0, 0x8c, 0xfe, 0x7f, 0x30, 0xed, 0xff, 0x81, 0xc, 0x4c, 0x93, 0x11, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfe, 0x97, 0x40, 0xa0, 0xa6, 0x84, 0xb1, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_right_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x18, 0x33, 0x61, 0x8c, 0x59, 0x2b, 0xa0, 0x8c, 0x5d, 0xab, 0xa0, 0x8c, 0xdd, 0xbb, 0x77, 0x40, 0x18, 0x67, 0xce, 0x9c, 0x80, 0x31, 0xa0, 0x52, 0x77, 0x6f, 0x40, 0x19, 0xef, 0x30, 0xc, 0x84, 0x30, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfd, 0x36, 0x40, 0x93, 0xf1, 0x83, 0x5f, 0xf2, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0x59, 0x59, 0x59, 0x8e, 0x47, 0x76, 0xf1, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x40, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0x51, 0x18, 0x33, 0x61, 0x8c, 0x59, 0x2b, 0xa0, 0x8c, 0x5d, 0xab, 0xa0, 0x8c, 0xdd, 0xbb, 0x77, 0x40, 0x18, 0x67, 0xce, 0x9c, 0x80, 0x31, 0xa0, 0x52, 0x77, 0x6f, 0x40, 0x19, 0xef, 0x30, 0xc, 0x84, 0x30, 0xe0, 0x96, 0x1a, 0x42, 0x9c, 0x21, 0x2, 0x0, 0xfd, 0x36, 0x40, 0x93, 0xf1, 0x83, 0x5f, 0xf2, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_right_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0x59, 0x59, 0x59, 0x56, 0xec, 0x9e, 0xdc, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x11, 0xe2, 0xb5, 0x3d, 0xba, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x85, 0xcf, 0xcb, 0xe, 0x80, 0x30, 0x8, 0x44, 0x51, 0xaa, 0xd2, 0x7, 0xb4, 0xd2, 0xff, 0xff, 0xda, 0xea, 0x6e, 0x46, 0x63, 0xbc, 0xcb, 0x93, 0x40, 0x40, 0x24, 0x29, 0x94, 0x36, 0xc9, 0xa5, 0x42, 0x25, 0x8b, 0x56, 0x4a, 0xbf, 0xa0, 0xb5, 0x7, 0x98, 0x19, 0x83, 0x77, 0xef, 0xc, 0x3e, 0xdc, 0x11, 0xc6, 0x1d, 0xc2, 0x79, 0x45, 0x23, 0x11, 0xc1, 0x4b, 0xe7, 0xfc, 0x3b, 0xc, 0xe0, 0xf5, 0xdc, 0xce, 0xef, 0x1f, 0xb, 0xc, 0x30, 0x7, 0xaf, 0x1f, 0x5b, 0x76, 0x12, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0x2e, 0x3d, 0xb1, 0x1e, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xc8, 0x31, 0x16, 0x2, 0x20, 0x10, 0x43, 0xc1, 0xe4, 0x83, 0xdc, 0xff, 0xb8, 0x88, 0xf, 0x57, 0xb, 0x8b, 0x80, 0x53, 0xe, 0xf2, 0x23, 0x18, 0xc6, 0x68, 0x61, 0x74, 0xca, 0xa, 0x2e, 0x74, 0xf9, 0x85, 0xfd, 0x17, 0x5c, 0x81, 0xfb, 0x11, 0x2a, 0xaa, 0x65, 0x80, 0x20, 0xc3, 0x5f, 0xaf, 0x2b, 0x96, 0xce, 0x78, 0xea, 0x88, 0x39, 0x95, 0x91, 0x70, 0x29, 0x94, 0xd9, 0x6b, 0x87, 0xf5, 0xfe, 0x0, 0xc6, 0xa7, 0x1b, 0x66, 0x7b, 0x42, 0xf1, 0x14, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_up_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0x59, 0x59, 0x59, 0xb3, 0x52, 0xf2, 0x5, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xa, 0x68, 0xd0, 0xf4, 0x56, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xb1, 0x33, 0x3a, 0x67, 0x40, 0x19, 0x33, 0x67, 0x42, 0x18, 0x9d, 0x33, 0x67, 0xce, 0x0, 0x33, 0x66, 0x2, 0x1, 0x2a, 0x3, 0x9f, 0x39, 0x10, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0x59, 0xc8, 0x3d, 0xf9, 0xf, 0x68, 0xc5, 0xa9, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x2d, 0x2c, 0x2f, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0x38, 0x36, 0x3a, 0xc3, 0xc3, 0xc3, 0x59, 0x59, 0x59, 0xb3, 0x52, 0xf2, 0x5, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x8, 0xfe, 0x9, 0xd, 0x19, 0x4a, 0xb6, 0xc1, 0xe6, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xb1, 0x33, 0x3a, 0x67, 0x40, 0x19, 0x33, 0x67, 0x42, 0x18, 0x9d, 0x33, 0x67, 0xce, 0x0, 0x33, 0x66, 0x2, 0x1, 0x2a, 0x3, 0x9f, 0x39, 0x10, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0x59, 0xc8, 0x3d, 0xf9, 0xf, 0x68, 0xc5, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_button_up_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xce, 0xce, 0xce, 0x59, 0x59, 0x59, 0xb8, 0xf5, 0x6d, 0x48, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xa, 0x68, 0xd0, 0xf4, 0x56, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xb1, 0x33, 0x3a, 0x67, 0x40, 0x19, 0x33, 0x67, 0x42, 0x18, 0x9d, 0x33, 0x67, 0xce, 0x0, 0x33, 0x66, 0x2, 0x1, 0x2a, 0x3, 0x9f, 0x39, 0x10, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0x59, 0xc8, 0x3d, 0xf9, 0xf, 0x68, 0xc5, 0xa9, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x21, 0x50, 0x4c, 0x54, 0x45, 0x3d, 0x3b, 0x3f, 0x60, 0x5d, 0x62, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x3d, 0x3b, 0x3f, 0x65, 0x62, 0x67, 0x60, 0x5d, 0x62, 0x56, 0x53, 0x58, 0x4b, 0x49, 0x4e, 0xce, 0xce, 0xce, 0x59, 0x59, 0x59, 0xb8, 0xf5, 0x6d, 0x48, 0x0, 0x0, 0x0, 0x5, 0x74, 0x52, 0x4e, 0x53, 0x7, 0xfe, 0xc, 0x9, 0x1c, 0xda, 0x2b, 0xa5, 0x57, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc, 0x5, 0x3, 0x21, 0x86, 0xf4, 0xe, 0x30, 0x28, 0x63, 0x88, 0x80, 0x30, 0x5a, 0xb1, 0x33, 0x3a, 0x67, 0x40, 0x19, 0x33, 0x67, 0x42, 0x18, 0x9d, 0x33, 0x67, 0xce, 0x0, 0x33, 0x66, 0x2, 0x1, 0x2a, 0x3, 0x9f, 0x39, 0x10, 0x6, 0xdc, 0x52, 0x43, 0x88, 0x33, 0x44, 0x0, 0x59, 0xc8, 0x3d, 0xf9, 0xf, 0x68, 0xc5, 0xa9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_grabber_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x60, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0x59, 0x61, 0x5b, 0x59, 0x61, 0x5a, 0x58, 0x60, 0x59, 0x57, 0x5f, 0x5a, 0x58, 0x60, 0x5a, 0x58, 0x60, 0x57, 0x56, 0x5e, 0x58, 0x56, 0x5e, 0x56, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x55, 0x53, 0x5b, 0x55, 0x53, 0x5b, 0x54, 0x53, 0x5b, 0x55, 0x54, 0x5c, 0x54, 0x52, 0x5a, 0x55, 0x53, 0x5b, 0x5a, 0x58, 0x60, 0x56, 0x54, 0x5c, 0x54, 0x53, 0x5a, 0x55, 0x53, 0x5b, 0x53, 0x51, 0x59, 0x52, 0x51, 0x59, 0x52, 0x50, 0x58, 0x51, 0x50, 0x58, 0x51, 0x4f, 0x57, 0x50, 0x4e, 0x56, 0x4f, 0x4d, 0x55, 0x50, 0x4f, 0x57, 0x54, 0x52, 0x5a, 0xff, 0xff, 0xff, 0xc7, 0x51, 0xc2, 0xf2, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2c, 0xb8, 0xf4, 0x2e, 0xf2, 0xb8, 0xf4, 0xf5, 0xf4, 0xf5, 0xb8, 0x2f, 0xf2, 0x2e, 0xb8, 0xf4, 0xb8, 0x66, 0xf6, 0xf7, 0x12, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1f, 0x5, 0xd, 0x10, 0xbd, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x50, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc0, 0x7, 0x18, 0x99, 0x98, 0x85, 0x98, 0x18, 0x21, 0x6c, 0x16, 0x56, 0x61, 0x11, 0x11, 0x61, 0x56, 0x16, 0x30, 0x87, 0x4d, 0x54, 0xc, 0x8, 0x44, 0xd9, 0xc0, 0x1c, 0x76, 0x71, 0x9, 0x20, 0x10, 0xe7, 0x0, 0x73, 0x38, 0x25, 0xa5, 0x80, 0x40, 0x92, 0xb, 0xcc, 0xe1, 0x96, 0x90, 0x6, 0x2, 0x9, 0x6e, 0x30, 0x87, 0x87, 0x57, 0x4a, 0x46, 0x46, 0x96, 0x97, 0x7, 0x62, 0x1c, 0x1f, 0xbf, 0x80, 0x9c, 0x20, 0x1f, 0x5e, 0xdb, 0x1, 0x23, 0xfd, 0x4, 0x11, 0x2d, 0x48, 0xcb, 0xd2, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x5d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0x59, 0x61, 0x5b, 0x59, 0x61, 0x5a, 0x58, 0x60, 0x59, 0x57, 0x5f, 0x5a, 0x58, 0x60, 0x5a, 0x58, 0x60, 0x57, 0x56, 0x5e, 0x58, 0x56, 0x5e, 0x56, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x57, 0x55, 0x5d, 0x55, 0x53, 0x5b, 0x55, 0x53, 0x5b, 0x54, 0x53, 0x5b, 0x55, 0x54, 0x5c, 0x54, 0x52, 0x5a, 0x55, 0x53, 0x5b, 0x5a, 0x58, 0x60, 0x56, 0x54, 0x5c, 0x54, 0x53, 0x5a, 0x55, 0x53, 0x5b, 0x53, 0x51, 0x59, 0x52, 0x51, 0x59, 0x52, 0x50, 0x58, 0x51, 0x50, 0x58, 0x51, 0x4f, 0x57, 0x50, 0x4e, 0x56, 0x4f, 0x4d, 0x55, 0x50, 0x4f, 0x57, 0x54, 0x52, 0x5a, 0xae, 0x55, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x12, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x2c, 0xb8, 0xf4, 0x2e, 0xf2, 0xb8, 0xf4, 0xf5, 0xf4, 0xf5, 0xb8, 0x2f, 0xf2, 0x2e, 0xb8, 0xf4, 0xb8, 0x66, 0xf6, 0xf7, 0x12, 0x0, 0x0, 0x0, 0x48, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x64, 0xc7, 0xb1, 0x11, 0x80, 0x40, 0x8, 0x45, 0x41, 0xff, 0x83, 0x2, 0xe, 0xfb, 0x2f, 0x13, 0xe2, 0xf3, 0x6, 0x12, 0x1d, 0x37, 0x5b, 0xae, 0x97, 0x5f, 0x84, 0xdd, 0x68, 0xe2, 0x1e, 0x41, 0xb8, 0x77, 0x58, 0x6, 0xb6, 0xe8, 0x88, 0xd1, 0xe1, 0x93, 0xad, 0x63, 0xab, 0xa3, 0x3a, 0xa3, 0x26, 0xa9, 0x4a, 0x52, 0xf9, 0xc, 0x62, 0xcf, 0xa7, 0x8f, 0x1f, 0x1e, 0xbd, 0xff, 0x84, 0xee, 0x2, 0x0, 0x54, 0x76, 0x10, 0x19, 0x1e, 0xd7, 0x1d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_grabber_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x97, 0xd0, 0xdf, 0x92, 0xcb, 0xdc, 0x84, 0xbb, 0xd4, 0x92, 0xca, 0xdc, 0x95, 0xd0, 0xdd, 0x83, 0xbb, 0xd3, 0x8b, 0xc8, 0xd7, 0x79, 0xb5, 0xcb, 0x78, 0xb4, 0xca, 0x73, 0xb0, 0xc7, 0x73, 0xb0, 0xc7, 0x7b, 0xc0, 0xcf, 0x79, 0xc5, 0xd1, 0x6b, 0xae, 0xc1, 0x75, 0xc6, 0xcf, 0x70, 0xbc, 0xca, 0x64, 0xa6, 0xbc, 0x71, 0xbc, 0xc9, 0x82, 0xba, 0xd4, 0x6a, 0xa2, 0xc6, 0x62, 0x9a, 0xc2, 0x61, 0x9a, 0xc1, 0x68, 0x9f, 0xc2, 0x5d, 0x92, 0xbb, 0x5c, 0x92, 0xb8, 0x58, 0x8d, 0xb6, 0x59, 0x8e, 0xb3, 0x56, 0x89, 0xb0, 0x5c, 0x91, 0xb2, 0x53, 0x84, 0xa9, 0x58, 0x8f, 0xae, 0x54, 0x83, 0xa4, 0x57, 0x8e, 0xad, 0x64, 0xa5, 0xba, 0xff, 0xff, 0xff, 0xbb, 0x65, 0x65, 0x27, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x25, 0xad, 0xf1, 0xad, 0x27, 0xef, 0xad, 0xf1, 0xf3, 0xf1, 0xf3, 0xad, 0x28, 0xef, 0x27, 0xad, 0xf2, 0xad, 0xcd, 0x8a, 0x27, 0xfe, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x23, 0x2a, 0x62, 0x6c, 0x3a, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x50, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc0, 0x7, 0x18, 0x99, 0x98, 0x85, 0x59, 0x18, 0x21, 0x6c, 0x56, 0x36, 0x11, 0x51, 0x31, 0x11, 0x36, 0x56, 0x30, 0x87, 0x5d, 0x5c, 0x2, 0x8, 0xc4, 0xd9, 0xc1, 0x1c, 0xe, 0x49, 0x29, 0x20, 0x90, 0xe4, 0x4, 0x73, 0xb8, 0xa4, 0x65, 0x80, 0x40, 0x9a, 0x1b, 0xcc, 0xe1, 0x91, 0x95, 0x3, 0x2, 0x59, 0x1e, 0x30, 0x87, 0x97, 0x4f, 0x5e, 0x41, 0x41, 0x91, 0x8f, 0x17, 0x62, 0x1c, 0xbf, 0x80, 0xa0, 0x92, 0x10, 0x3f, 0x5e, 0xdb, 0x1, 0x41, 0x87, 0x4, 0x7d, 0x15, 0xc4, 0xfd, 0x6a, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x3, 0x0, 0x0, 0x0, 0x61, 0xab, 0xac, 0xd5, 0x0, 0x0, 0x0, 0x69, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x97, 0xd0, 0xdf, 0x92, 0xcb, 0xdc, 0x84, 0xbb, 0xd4, 0x92, 0xca, 0xdc, 0x95, 0xd0, 0xdd, 0x83, 0xbb, 0xd3, 0x8b, 0xc8, 0xd7, 0x79, 0xb5, 0xcb, 0x78, 0xb4, 0xca, 0x73, 0xb0, 0xc7, 0x73, 0xb0, 0xc7, 0x7b, 0xc0, 0xcf, 0x79, 0xc5, 0xd1, 0x6b, 0xae, 0xc1, 0x75, 0xc6, 0xcf, 0x70, 0xbc, 0xca, 0x64, 0xa6, 0xbc, 0x71, 0xbc, 0xc9, 0x82, 0xba, 0xd4, 0x6a, 0xa2, 0xc6, 0x62, 0x9a, 0xc2, 0x61, 0x9a, 0xc1, 0x68, 0x9f, 0xc2, 0x5d, 0x92, 0xbb, 0x5c, 0x92, 0xb8, 0x58, 0x8d, 0xb6, 0x59, 0x8e, 0xb3, 0x56, 0x89, 0xb0, 0x5c, 0x91, 0xb2, 0x53, 0x84, 0xa9, 0x58, 0x8f, 0xae, 0x54, 0x83, 0xa4, 0x57, 0x8e, 0xad, 0x64, 0xa5, 0xba, 0x17, 0x3b, 0xfc, 0x67, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x25, 0xad, 0xf1, 0xad, 0x27, 0xef, 0xad, 0xf1, 0xf3, 0xf1, 0xf3, 0xad, 0x28, 0xef, 0x27, 0xad, 0xf2, 0xad, 0xcd, 0x8a, 0x27, 0xfe, 0x0, 0x0, 0x0, 0x49, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x75, 0xc7, 0xa5, 0x1, 0x3, 0x1, 0x0, 0x4, 0xc1, 0xdd, 0xb, 0x9a, 0x60, 0xff, 0x3d, 0x6, 0x54, 0xe0, 0x59, 0x3d, 0x8f, 0x9b, 0xb0, 0x66, 0x3, 0x98, 0xdc, 0xff, 0x35, 0x20, 0xec, 0x3c, 0x6b, 0xf5, 0xae, 0xff, 0x6c, 0xda, 0xdc, 0x36, 0x31, 0xc7, 0x6f, 0xc9, 0x16, 0x8c, 0x40, 0x1d, 0xba, 0x64, 0x21, 0x42, 0xc0, 0x97, 0xc9, 0xe6, 0x25, 0x8, 0x5c, 0xf4, 0xf6, 0x2c, 0x5f, 0x8c, 0x39, 0x4c, 0x3, 0xfe, 0x9a, 0x10, 0x43, 0x82, 0xcf, 0x27, 0x93, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char scroll_grabber_pressed_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x6, 0x0, 0x0, 0x0, 0x56, 0x75, 0x5c, 0xe7, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xd, 0xd7, 0x0, 0x0, 0xd, 0xd7, 0x1, 0x42, 0x28, 0x9b, 0x78, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x0, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x6e, 0x6b, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2e, 0x6f, 0x72, 0x67, 0x9b, 0xee, 0x3c, 0x1a, 0x0, 0x0, 0x0, 0x66, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0xcd, 0x91, 0xb1, 0xa, 0xc0, 0x20, 0x10, 0x43, 0x63, 0x71, 0xbf, 0x5f, 0x12, 0x9c, 0xfd, 0x1a, 0x3f, 0xcd, 0xa9, 0x83, 0xe0, 0x2f, 0x65, 0x2f, 0x9c, 0x8b, 0x83, 0x47, 0xed, 0x60, 0xbb, 0x34, 0xdb, 0x3d, 0x12, 0x48, 0x38, 0xa7, 0xaa, 0xd8, 0xd1, 0xb1, 0xe5, 0x7e, 0x13, 0xf0, 0xf3, 0xd1, 0x5a, 0xf3, 0x24, 0x23, 0x80, 0x34, 0x50, 0x11, 0x91, 0x1a, 0x42, 0xb8, 0x96, 0x1, 0x92, 0x51, 0x55, 0xf3, 0x84, 0x32, 0x49, 0x0, 0x38, 0x9f, 0x2a, 0x25, 0xdc, 0x65, 0xd8, 0xe7, 0xd1, 0x65, 0xe1, 0x31, 0xcc, 0x6c, 0x10, 0x91, 0x3a, 0x3a, 0x9b, 0xd1, 0xb3, 0xc7, 0xfd, 0xef, 0x71, 0x1d, 0x42, 0xe6, 0x21, 0x43, 0xf5, 0x2b, 0xd8, 0x6c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x3a, 0xd8, 0xcf, 0xb2, 0xc1, 0x7d, 0xc3, 0x14, 0x20, 0x74, 0xdf, 0xcf, 0x82, 0x22, 0xb1, 0xc1, 0x7d, 0xfd, 0x2e, 0x8, 0xdc, 0xe0, 0x8e, 0x2a, 0x31, 0x5, 0x2e, 0x31, 0x85, 0x90, 0x4, 0xa6, 0x51, 0xb8, 0x2d, 0xa7, 0x36, 0x0, 0x0, 0x7b, 0xcd, 0x2b, 0x75, 0x45, 0x5e, 0xf8, 0x88, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char selection_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfd, 0xfb, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf6, 0xff, 0xf6, 0xf4, 0xff, 0x15, 0x15, 0x17, 0xff, 0x70, 0xc0, 0x21, 0x0, 0x0, 0x0, 0xe, 0x74, 0x52, 0x4e, 0x53, 0x6, 0xf, 0x16, 0x18, 0x2a, 0x3b, 0x40, 0x3c, 0x6, 0x3d, 0x44, 0x3e, 0x31, 0x25, 0x8, 0x3d, 0x16, 0xb4, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xe, 0x6f, 0xbd, 0x30, 0x4f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x6, 0x2, 0x23, 0x1, 0x6, 0x91, 0xb0, 0x34, 0x20, 0x48, 0x75, 0x64, 0x50, 0xef, 0x5c, 0x5, 0x4, 0x33, 0x8a, 0x18, 0xcc, 0xf6, 0xdc, 0x5, 0x82, 0xd3, 0xc9, 0xc, 0x66, 0x6b, 0x41, 0x8c, 0x5b, 0x94, 0x33, 0x60, 0x6, 0xc2, 0xad, 0x80, 0x5b, 0xa, 0x73, 0x6, 0x0, 0x45, 0x34, 0x48, 0x41, 0xa3, 0xc5, 0x91, 0x23, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfd, 0xfb, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf7, 0xff, 0xfd, 0xf6, 0xff, 0xf6, 0xf4, 0xff, 0x15, 0x15, 0x17, 0xff, 0x70, 0xc0, 0x21, 0x0, 0x0, 0x0, 0xe, 0x74, 0x52, 0x4e, 0x53, 0x6, 0xf, 0x16, 0x18, 0x2a, 0x3b, 0x40, 0x3c, 0x6, 0x3d, 0x44, 0x3e, 0x31, 0x25, 0x8, 0x3d, 0x16, 0xb4, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x6, 0x2, 0x23, 0x1, 0x6, 0x91, 0xb0, 0x34, 0x20, 0x48, 0x75, 0x64, 0x50, 0xef, 0x5c, 0x5, 0x4, 0x33, 0x8a, 0x18, 0xcc, 0xf6, 0xdc, 0x5, 0x82, 0xd3, 0xc9, 0xc, 0x66, 0x6b, 0x41, 0x8c, 0x5b, 0x94, 0x33, 0x60, 0x6, 0xc2, 0xad, 0x80, 0x5b, 0xa, 0x73, 0x6, 0x0, 0x45, 0x34, 0x48, 0x41, 0xa3, 0xc5, 0x91, 0x23, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char selection_oof_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0x15, 0x15, 0x17, 0xe9, 0x54, 0x1, 0x21, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xf, 0x18, 0xba, 0x0, 0xd9, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x6, 0x8, 0x76, 0x4d, 0x62, 0x70, 0xdf, 0xfb, 0xe, 0x8, 0x6e, 0x97, 0x30, 0x78, 0x9c, 0x3, 0x31, 0xde, 0xb4, 0x50, 0xca, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xcc, 0x19, 0x0, 0x1a, 0x23, 0x52, 0x59, 0xa4, 0x2f, 0x3d, 0xa7, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char spinbox_updown_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xcd, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xe5, 0x53, 0x31, 0xe, 0x82, 0x40, 0x10, 0x9c, 0x5d, 0x22, 0x3e, 0x80, 0x86, 0xc4, 0x0, 0xe1, 0xf, 0x16, 0x5a, 0x58, 0xf9, 0x5, 0xe3, 0x13, 0xd0, 0xc2, 0xc4, 0xcf, 0xd8, 0xf0, 0x5, 0xe3, 0x1f, 0x2c, 0x2c, 0x6c, 0xf8, 0x1, 0xc5, 0x72, 0x85, 0x9, 0xd, 0xf, 0x10, 0x43, 0xce, 0x86, 0x82, 0xe0, 0xe9, 0x19, 0x8d, 0x95, 0x53, 0xce, 0xce, 0x6e, 0x26, 0x99, 0x59, 0xe0, 0x97, 0x10, 0x91, 0x95, 0x52, 0x2a, 0x79, 0xa5, 0xa1, 0x67, 0x83, 0xa2, 0x28, 0xa6, 0x0, 0x8e, 0x0, 0x98, 0x99, 0xe7, 0x61, 0x18, 0x9e, 0xde, 0x3e, 0x20, 0x22, 0x3e, 0x11, 0x65, 0x0, 0x46, 0x2d, 0x55, 0x3a, 0x8e, 0x33, 0xe, 0x82, 0xe0, 0xd2, 0xd7, 0x72, 0x9f, 0xc8, 0xb2, 0x6c, 0x0, 0x60, 0xdf, 0x59, 0x6, 0x0, 0xbf, 0x69, 0x9a, 0x43, 0x9e, 0xe7, 0x43, 0xeb, 0x1, 0xcf, 0xf3, 0x76, 0x44, 0x34, 0x33, 0x18, 0x9b, 0xb8, 0xae, 0x9b, 0x9a, 0x1c, 0xff, 0x3b, 0x1e, 0x62, 0x14, 0x91, 0x94, 0x88, 0x8c, 0xe5, 0x21, 0xa2, 0x34, 0x8a, 0xa2, 0x75, 0x97, 0x7b, 0x48, 0xa1, 0xaa, 0xaa, 0x8d, 0xd6, 0xda, 0x54, 0x9a, 0x73, 0x5d, 0xd7, 0x5b, 0xab, 0x83, 0xd6, 0xc5, 0xe7, 0x45, 0x2, 0x80, 0x38, 0x8e, 0x4b, 0xad, 0xf5, 0x2, 0xc0, 0x15, 0xc0, 0x8d, 0x99, 0x97, 0xa6, 0x65, 0x2b, 0x94, 0x52, 0x89, 0xed, 0x99, 0xbe, 0xc6, 0x1d, 0x31, 0x1f, 0x40, 0xdc, 0x74, 0x8a, 0x5b, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char submenu_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xf, 0xbe, 0x8b, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x7d, 0xca, 0x21, 0x12, 0x80, 0x20, 0x14, 0x45, 0xd1, 0xf7, 0x2d, 0x4, 0x36, 0x40, 0x63, 0xa8, 0xba, 0x1f, 0xbb, 0x9d, 0xe5, 0xb8, 0x0, 0xb3, 0xfb, 0xd1, 0xc, 0x8d, 0xd, 0x10, 0x48, 0xcf, 0x22, 0x6, 0xc7, 0xef, 0x6d, 0x77, 0xe6, 0x8, 0x0, 0xa4, 0x94, 0x88, 0x3b, 0x92, 0x4b, 0x8, 0x61, 0xeb, 0x3f, 0xe0, 0x95, 0x88, 0xac, 0x39, 0xe7, 0x49, 0x5, 0x0, 0x2c, 0xc9, 0xbd, 0x94, 0x62, 0x35, 0x0, 0x0, 0x63, 0x6b, 0x6d, 0xfd, 0x3, 0x4f, 0x1a, 0x38, 0x8d, 0x31, 0x51, 0x3, 0x55, 0x44, 0x66, 0xe7, 0x5c, 0xfd, 0x4, 0x24, 0xa3, 0xf7, 0xfe, 0xe8, 0x7f, 0x1, 0xe, 0xc2, 0x1e, 0x10, 0xa, 0xf0, 0x33, 0x4c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x78, 0xc0, 0xf0, 0xe0, 0x3f, 0x8, 0xde, 0x4f, 0x60, 0x0, 0x3, 0xb8, 0xc0, 0x83, 0x2f, 0xf, 0xb5, 0xe1, 0x2, 0x50, 0x78, 0xf5, 0x5, 0x37, 0xaa, 0xc0, 0xff, 0x87, 0xf3, 0x31, 0x4, 0x30, 0xb5, 0x60, 0x1a, 0x8a, 0x61, 0x2d, 0x0, 0xa6, 0x55, 0x4f, 0xb1, 0x91, 0xd6, 0xa7, 0xae, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x0, 0xaa, 0x8d, 0x23, 0x32, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0xc0, 0x4, 0xff, 0x23, 0xff, 0x8b, 0xfc, 0x17, 0xf9, 0x1f, 0x49, 0xac, 0x10, 0x13, 0x3, 0x3, 0x61, 0x53, 0xb0, 0x98, 0x80, 0xc, 0x0, 0xa8, 0x3e, 0x18, 0x31, 0xbe, 0x78, 0xfc, 0x7a, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x4, 0x0, 0x0, 0x0, 0x6e, 0x6, 0x76, 0x0, 0x0, 0x0, 0x0, 0x19, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xc0, 0x2, 0xfe, 0x47, 0xfe, 0x17, 0x1, 0xc2, 0x48, 0xd2, 0x84, 0x10, 0x2, 0x84, 0xb9, 0x98, 0x0, 0x0, 0xbf, 0x67, 0x1d, 0x5, 0x89, 0x9b, 0x48, 0x90, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_behind_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x5a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x2e, 0x36, 0x43, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x43, 0x40, 0x4c, 0x3e, 0x3c, 0x47, 0x3e, 0x3b, 0x46, 0x31, 0x2f, 0x38, 0x2d, 0x2b, 0x33, 0x3f, 0x3c, 0x47, 0x35, 0x32, 0x3b, 0x5b, 0xb0, 0x1, 0xb7, 0x0, 0x0, 0x0, 0x18, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0xc, 0x4, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x77, 0xf3, 0x7, 0xef, 0xd3, 0x51, 0x5e, 0xca, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1d, 0xeb, 0x3, 0x71, 0x91, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x6e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0xb5, 0xcc, 0x49, 0xe, 0x80, 0x20, 0x10, 0x44, 0xd1, 0x2, 0x6c, 0x90, 0x49, 0x26, 0x27, 0xd4, 0xfb, 0x9f, 0x53, 0x63, 0x34, 0x2e, 0x58, 0xfb, 0x97, 0x2f, 0xa9, 0x2, 0x18, 0x17, 0x1d, 0x49, 0xa5, 0x24, 0x75, 0x82, 0x33, 0x80, 0xf5, 0xa4, 0x8d, 0x75, 0xde, 0x3b, 0x6b, 0x34, 0xf5, 0xc, 0x9c, 0x86, 0x10, 0x53, 0x2e, 0x25, 0xa7, 0x18, 0x6, 0xe2, 0x10, 0x3a, 0x8c, 0xd3, 0x5a, 0xaf, 0xd6, 0x69, 0xc, 0x5a, 0x60, 0x36, 0x71, 0xd9, 0xf6, 0xbb, 0x6d, 0x89, 0x66, 0x6, 0xd9, 0x74, 0xec, 0x4f, 0x47, 0xb2, 0x4, 0xe9, 0x72, 0x7d, 0xa1, 0x66, 0x27, 0xa1, 0x7c, 0xf9, 0xa0, 0x78, 0xd5, 0x42, 0x33, 0x69, 0x4e, 0xff, 0x80, 0x13, 0xce, 0x8, 0x12, 0xa9, 0x90, 0xd8, 0x47, 0x93, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x57, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x2e, 0x36, 0x43, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x43, 0x40, 0x4c, 0x3e, 0x3c, 0x47, 0x3e, 0x3b, 0x46, 0x31, 0x2f, 0x38, 0x2d, 0x2b, 0x33, 0x3f, 0x3c, 0x47, 0x91, 0xf8, 0xc4, 0xb2, 0x0, 0x0, 0x0, 0x18, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x3, 0x5, 0x8, 0xa, 0xb, 0xc, 0x4, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x77, 0xf3, 0x7, 0xef, 0xd3, 0x51, 0x5e, 0xca, 0x0, 0x0, 0x0, 0x5a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0x8c, 0x35, 0x2, 0x80, 0x30, 0x10, 0x4, 0x6f, 0x73, 0xc4, 0x53, 0xf3, 0xff, 0x3f, 0xe2, 0xee, 0x4e, 0x9d, 0xe9, 0x56, 0x41, 0xd8, 0xa1, 0x69, 0x7, 0xd0, 0x43, 0x72, 0x19, 0x3d, 0x37, 0x10, 0x6c, 0x1f, 0x8d, 0x6a, 0x0, 0x2b, 0x25, 0xc1, 0x20, 0xa2, 0x69, 0x98, 0xba, 0xb6, 0x45, 0x9a, 0x2b, 0xbd, 0xe9, 0xd5, 0x69, 0xda, 0x0, 0xa9, 0x94, 0x9f, 0x68, 0x7, 0xc5, 0xd2, 0x50, 0x4a, 0x69, 0x71, 0x18, 0x63, 0xb3, 0x18, 0x7a, 0x71, 0x2e, 0xa3, 0xfd, 0x1b, 0xff, 0xc9, 0xff, 0x34, 0x86, 0x31, 0x3, 0x12, 0xb2, 0x4c, 0x6a, 0xfb, 0x60, 0xc7, 0xdc, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_close_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xfa, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xcd, 0x92, 0x5f, 0x4a, 0xc4, 0x30, 0x10, 0x87, 0xbf, 0xb1, 0xb9, 0xc2, 0x16, 0x7c, 0x6b, 0xc1, 0xa2, 0x85, 0x7a, 0x4, 0x2d, 0xfe, 0x39, 0xc4, 0x9e, 0x70, 0x4f, 0x61, 0xc5, 0x3d, 0x83, 0x5, 0x95, 0x4, 0xd2, 0x37, 0x5, 0x8f, 0x90, 0x94, 0xf1, 0xc5, 0x4a, 0x76, 0xcd, 0x22, 0xf8, 0xa2, 0xf3, 0x38, 0xc3, 0xef, 0x9b, 0xe4, 0x4b, 0xe0, 0x5f, 0x95, 0xf7, 0x7e, 0xed, 0x9c, 0x2b, 0xf, 0xcd, 0x9d, 0x73, 0xa5, 0xf7, 0x7e, 0x9d, 0xf6, 0x8e, 0xd2, 0xb0, 0x88, 0x6c, 0x8c, 0x31, 0x43, 0xe, 0xe2, 0x9c, 0x2b, 0x8d, 0x31, 0x83, 0x88, 0x6c, 0x52, 0xc8, 0x17, 0x20, 0xc6, 0x38, 0xa8, 0xea, 0x8, 0x74, 0xc6, 0x98, 0xed, 0x34, 0x4d, 0xc7, 0xcb, 0xcc, 0x5a, 0xbb, 0x2a, 0x8a, 0xe2, 0xe, 0xe8, 0x80, 0x17, 0xe0, 0x61, 0x99, 0x49, 0xba, 0xc5, 0x5a, 0xbb, 0xfa, 0xdc, 0x72, 0xe, 0x3c, 0x3, 0xd7, 0x21, 0x84, 0x98, 0xf6, 0x54, 0xf5, 0xaa, 0xae, 0xeb, 0xb7, 0x2c, 0x60, 0x1f, 0x22, 0x22, 0x56, 0x55, 0x23, 0xd0, 0xe6, 0xc2, 0x59, 0x40, 0x2, 0xd9, 0x8a, 0x48, 0xbb, 0x28, 0x50, 0xd5, 0x8b, 0xfd, 0xf0, 0x8e, 0x83, 0x9f, 0x4a, 0x44, 0xb2, 0xcb, 0xbe, 0x1, 0x92, 0x2b, 0xb4, 0xaa, 0x6a, 0x81, 0x27, 0xe0, 0x4, 0xb8, 0x4f, 0xc5, 0x66, 0x1, 0x19, 0x89, 0x97, 0x21, 0x84, 0x5e, 0x55, 0x1f, 0x81, 0xb3, 0x1c, 0x44, 0xe, 0x85, 0x53, 0x61, 0xb9, 0xd7, 0xa9, 0xaa, 0xea, 0x75, 0xe7, 0x4, 0xc6, 0x98, 0x1b, 0x11, 0xe9, 0x80, 0x31, 0xc6, 0xd8, 0xa7, 0xc2, 0x9a, 0xa6, 0x79, 0x9f, 0xe7, 0xf9, 0x16, 0x18, 0x81, 0x53, 0x55, 0xed, 0xb3, 0xa2, 0x7e, 0xf3, 0x95, 0xff, 0xbe, 0x3e, 0x0, 0xbd, 0x2c, 0x93, 0xec, 0xb, 0xe5, 0x4f, 0xb1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xad, 0x90, 0x1, 0x6, 0xc0, 0x30, 0xc, 0x45, 0x77, 0x89, 0xd5, 0x76, 0xb3, 0x9e, 0x7b, 0x65, 0x63, 0xd, 0xf9, 0xbb, 0x48, 0x3b, 0xb3, 0x92, 0x54, 0x42, 0xb1, 0x5, 0x88, 0xf7, 0xc8, 0xcf, 0x9f, 0xfe, 0x1a, 0x8e, 0x14, 0xf4, 0x4e, 0x81, 0x63, 0x87, 0x51, 0x90, 0x28, 0x8, 0x46, 0x42, 0x51, 0x4a, 0x9e, 0x79, 0x43, 0xc5, 0x81, 0x55, 0x6f, 0xbc, 0x34, 0xdc, 0x2b, 0x2e, 0x16, 0xe5, 0x3a, 0xb1, 0xb, 0xb6, 0xca, 0x3, 0x2b, 0xb2, 0xc2, 0xbe, 0xf0, 0x66, 0x71, 0x4f, 0x70, 0x3b, 0x61, 0x14, 0x89, 0x26, 0x71, 0x5d, 0x6c, 0x9f, 0x1e, 0x17, 0x35, 0xae, 0xfa, 0xeb, 0xdc, 0x62, 0xc3, 0x84, 0x2d, 0x77, 0x22, 0xda, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_container_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x8a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0xff, 0xff, 0xff, 0xe5, 0x37, 0x10, 0x78, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x2d, 0xcd, 0xda, 0x41, 0x3d, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x93, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x65, 0xcf, 0x47, 0x12, 0x82, 0x40, 0x10, 0x5, 0xd0, 0x9e, 0xc8, 0x44, 0x92, 0x22, 0x41, 0x54, 0x44, 0x40, 0x14, 0xef, 0x7f, 0x3e, 0x7, 0x8a, 0xea, 0x85, 0xbe, 0xe5, 0xaf, 0xea, 0xf0, 0x1, 0x8, 0x65, 0x5c, 0xc8, 0x40, 0x70, 0x46, 0x9, 0x0, 0x89, 0x94, 0x36, 0xd6, 0x79, 0xef, 0xac, 0xd1, 0x2a, 0x22, 0x40, 0x55, 0x9c, 0x14, 0xa7, 0x4d, 0x91, 0xc4, 0x8a, 0x2, 0xd3, 0x69, 0x59, 0xd5, 0x9b, 0xaa, 0x4c, 0x35, 0x3, 0x6e, 0x9a, 0xfa, 0xbc, 0xab, 0x1b, 0xc3, 0x41, 0xd8, 0xf6, 0x82, 0x5a, 0x2b, 0x40, 0xba, 0xeb, 0xd, 0x5d, 0x9d, 0x4, 0xe9, 0xbb, 0x3b, 0xea, 0xfc, 0x1a, 0xf4, 0xf, 0xd4, 0xaf, 0x81, 0x1b, 0x46, 0x34, 0x84, 0x11, 0x61, 0xa7, 0x27, 0x9a, 0xc2, 0x52, 0x6e, 0xe6, 0x17, 0x9a, 0xc3, 0x59, 0xa6, 0xb3, 0xf1, 0xbd, 0x1b, 0xb3, 0xf0, 0x18, 0x55, 0xf9, 0x61, 0xf9, 0x6c, 0x96, 0x63, 0x1e, 0x5e, 0xff, 0x2b, 0xf7, 0x5b, 0xff, 0xb, 0x69, 0x5a, 0x14, 0xfa, 0x84, 0xf6, 0xc2, 0x8, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x87, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x3a, 0x44, 0x56, 0x53, 0x61, 0x56, 0x52, 0x60, 0x47, 0x44, 0x52, 0x33, 0x31, 0x39, 0x47, 0x44, 0x50, 0x47, 0x44, 0x51, 0x52, 0x50, 0x5d, 0x51, 0x4f, 0x5d, 0x46, 0x42, 0x4e, 0x42, 0x3e, 0x4a, 0x41, 0x3e, 0x49, 0x51, 0x4e, 0x5b, 0x40, 0x3e, 0x48, 0x4f, 0x4c, 0x59, 0x3f, 0x3d, 0x47, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x47, 0x43, 0x50, 0x38, 0x35, 0x3f, 0x36, 0x34, 0x3e, 0x44, 0x42, 0x4d, 0x44, 0x41, 0x4c, 0x3f, 0x38, 0xaa, 0x5e, 0x0, 0x0, 0x0, 0x15, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xef, 0x77, 0xef, 0xed, 0xe8, 0xff, 0x76, 0xed, 0x0, 0x0, 0x0, 0x65, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x65, 0xcf, 0x45, 0x2, 0x84, 0x30, 0x14, 0x4, 0xd1, 0x2e, 0x1c, 0xee, 0x7f, 0xca, 0xd1, 0xed, 0x28, 0x8d, 0x4b, 0x92, 0x5a, 0xbe, 0xe8, 0x2f, 0xc4, 0x9c, 0x24, 0xcf, 0x15, 0x54, 0xab, 0x78, 0xee, 0x53, 0x30, 0x4a, 0x85, 0xa6, 0xfc, 0xf1, 0x87, 0x11, 0xb2, 0x9a, 0x15, 0x9a, 0x37, 0x13, 0x74, 0xce, 0xb4, 0xd4, 0x77, 0xcb, 0xe, 0xb4, 0x96, 0x99, 0x10, 0x34, 0x81, 0x42, 0x50, 0x21, 0x9d, 0x41, 0x23, 0xf8, 0xc, 0x56, 0xe1, 0x10, 0x9c, 0x40, 0x4e, 0xfe, 0x6e, 0x72, 0x96, 0x7e, 0xd7, 0xdf, 0x3f, 0xb3, 0x79, 0x90, 0xcd, 0xf1, 0xc4, 0x26, 0x1e, 0x8e, 0x78, 0xfc, 0x1, 0xf5, 0x61, 0x3f, 0x44, 0xe8, 0xf1, 0xdd, 0xba, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_current_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3d, 0x48, 0x5b, 0x58, 0x66, 0x5b, 0x57, 0x65, 0x57, 0x54, 0x62, 0x55, 0x53, 0x62, 0x4a, 0x46, 0x52, 0x46, 0x41, 0x4e, 0x45, 0x41, 0x4d, 0x55, 0x52, 0x60, 0x44, 0x41, 0x4c, 0x53, 0x50, 0x5e, 0x43, 0x40, 0x4b, 0x52, 0x4e, 0x5d, 0x41, 0x3e, 0x4a, 0x4f, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x4e, 0x4b, 0x59, 0x3e, 0x3c, 0x47, 0x4d, 0x4a, 0x58, 0x3d, 0x3b, 0x46, 0x4b, 0x49, 0x54, 0x3c, 0x3a, 0x44, 0x4b, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x3b, 0x39, 0x42, 0x3b, 0x38, 0x43, 0x3b, 0x38, 0x42, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3a, 0x38, 0x41, 0x39, 0x36, 0x3f, 0x38, 0x36, 0x3f, 0x39, 0x36, 0x40, 0x38, 0x36, 0x40, 0x37, 0x35, 0x3e, 0x37, 0x34, 0x3e, 0x36, 0x35, 0x3d, 0x35, 0x32, 0x3b, 0x59, 0xdd, 0xd3, 0xff, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xa3, 0x31, 0x6b, 0xc2, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x33, 0x37, 0xd5, 0x7c, 0x5e, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xa2, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x45, 0xcd, 0xd9, 0x12, 0x82, 0x30, 0xc, 0x40, 0xd1, 0x0, 0x2d, 0x4b, 0x5b, 0x36, 0x59, 0x44, 0x44, 0x44, 0xa4, 0x68, 0x59, 0x54, 0xfc, 0xff, 0x8f, 0x33, 0x30, 0x4c, 0x3d, 0x93, 0xa7, 0x3b, 0x93, 0x4, 0xc0, 0x30, 0x2d, 0x42, 0x6d, 0x44, 0x89, 0x65, 0x1a, 0x0, 0x86, 0xe3, 0x7a, 0x8c, 0xb, 0xdf, 0x17, 0x9c, 0x79, 0xae, 0x63, 0x80, 0xe9, 0x6, 0x61, 0x7c, 0xd8, 0xc4, 0x61, 0xe0, 0x9a, 0x60, 0x79, 0x51, 0x92, 0x66, 0x9b, 0x34, 0x89, 0x3c, 0xb, 0x8, 0xcb, 0xb3, 0xe3, 0x2e, 0xcb, 0x19, 0x1, 0xca, 0x8b, 0x93, 0x56, 0x70, 0xa, 0xb6, 0x28, 0xcf, 0x5a, 0x29, 0x6c, 0xb0, 0xfd, 0xea, 0xa2, 0x55, 0xfe, 0x1a, 0xea, 0xab, 0x56, 0xaf, 0x41, 0x34, 0x37, 0xad, 0xc1, 0x15, 0xca, 0xdb, 0xbb, 0xd6, 0xe2, 0x51, 0xc2, 0xba, 0x7f, 0xe8, 0xf0, 0x2d, 0x6, 0x29, 0xfb, 0x5e, 0xca, 0xc7, 0x53, 0xca, 0x3d, 0xa8, 0x61, 0x50, 0xc3, 0xa8, 0xc6, 0x41, 0xed, 0x61, 0x9a, 0xa6, 0x19, 0xbd, 0xe6, 0xf7, 0x1e, 0x3e, 0xcb, 0x82, 0x83, 0xbe, 0x18, 0x7e, 0xa1, 0xe5, 0x17, 0x1f, 0xcf, 0x5d, 0x82, 0x6b, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x3d, 0x48, 0x5b, 0x58, 0x66, 0x5b, 0x57, 0x65, 0x57, 0x54, 0x62, 0x55, 0x53, 0x62, 0x4a, 0x46, 0x52, 0x46, 0x41, 0x4e, 0x45, 0x41, 0x4d, 0x55, 0x52, 0x60, 0x44, 0x41, 0x4c, 0x53, 0x50, 0x5e, 0x43, 0x40, 0x4b, 0x52, 0x4e, 0x5d, 0x41, 0x3e, 0x4a, 0x4f, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x4e, 0x4b, 0x59, 0x3e, 0x3c, 0x47, 0x4d, 0x4a, 0x58, 0x3d, 0x3b, 0x46, 0x4b, 0x49, 0x54, 0x3c, 0x3a, 0x44, 0x4b, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x3b, 0x39, 0x42, 0x3b, 0x38, 0x43, 0x3b, 0x38, 0x42, 0x3a, 0x37, 0x41, 0x39, 0x37, 0x41, 0x3a, 0x38, 0x41, 0x39, 0x36, 0x3f, 0x38, 0x36, 0x3f, 0x39, 0x36, 0x40, 0x38, 0x36, 0x40, 0x37, 0x35, 0x3e, 0x37, 0x34, 0x3e, 0x36, 0x35, 0x3d, 0xd7, 0x41, 0xa4, 0x19, 0x0, 0x0, 0x0, 0x11, 0x74, 0x52, 0x4e, 0x53, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x77, 0xef, 0xef, 0xa3, 0x31, 0x6b, 0xc2, 0x0, 0x0, 0x0, 0x60, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x55, 0xca, 0x85, 0xd, 0xc0, 0x40, 0x14, 0xc3, 0x50, 0x27, 0xf7, 0xd5, 0xfd, 0xd7, 0x2d, 0xa6, 0x4c, 0x16, 0x3f, 0xb9, 0xd0, 0x11, 0x90, 0xa3, 0x52, 0x77, 0x49, 0x8e, 0x86, 0xd2, 0x26, 0x16, 0x7b, 0x59, 0x32, 0x68, 0x3, 0x37, 0x5d, 0xe0, 0x59, 0x3b, 0x74, 0x31, 0x67, 0x4b, 0x3b, 0xf, 0x71, 0xe5, 0xe8, 0xf, 0xec, 0xc0, 0x1f, 0x28, 0xf8, 0x2, 0x14, 0xf9, 0x42, 0xa8, 0xfc, 0x21, 0x3b, 0xe4, 0x1, 0x6f, 0x0, 0x18, 0x11, 0xac, 0x99, 0xc0, 0xe, 0x25, 0x22, 0x2d, 0x76, 0xc6, 0x13, 0x1a, 0x8, 0xac, 0x78, 0xfc, 0x1c, 0x70, 0x30, 0x2b, 0xba, 0xe9, 0x31, 0x70, 0xc1, 0x7f, 0x3b, 0x77, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_menu_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x6f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x80, 0x81, 0x81, 0x11, 0x5d, 0xe0, 0xc1, 0x83, 0x7, 0xff, 0xf1, 0x69, 0x50, 0x50, 0x50, 0x40, 0xd1, 0xc3, 0x44, 0xa9, 0xb, 0xa8, 0x6f, 0x0, 0x23, 0x23, 0x63, 0x3c, 0x3, 0x3, 0xc3, 0x57, 0x2c, 0x6a, 0xbf, 0x33, 0x32, 0x32, 0xa6, 0x63, 0xa8, 0xc7, 0x66, 0xea, 0xfd, 0xfb, 0xf7, 0x35, 0x18, 0x18, 0x18, 0x56, 0x31, 0x32, 0x32, 0xea, 0x42, 0x85, 0x6e, 0x30, 0x33, 0x33, 0x87, 0xc9, 0xca, 0xca, 0x5e, 0x26, 0xca, 0x0, 0x6, 0x6, 0x6, 0x86, 0x17, 0x2f, 0x5e, 0x70, 0xff, 0xfc, 0xf9, 0x73, 0xa, 0x3, 0x3, 0x3, 0x3, 0x3b, 0x3b, 0x7b, 0x8e, 0x84, 0x84, 0x4, 0x36, 0x57, 0xd, 0x2, 0x0, 0x0, 0x67, 0xf2, 0x14, 0xc2, 0xc2, 0xbe, 0xf5, 0xb5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tab_menu_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x6f, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0x63, 0x60, 0x18, 0x5, 0xa3, 0x80, 0x81, 0x81, 0x11, 0x5d, 0xe0, 0xc1, 0x83, 0x7, 0xff, 0xf1, 0x69, 0x50, 0x50, 0x50, 0x40, 0xd1, 0xc3, 0x44, 0xa9, 0xb, 0xa8, 0x6f, 0x0, 0x23, 0x23, 0x63, 0x3c, 0x3, 0x3, 0xc3, 0x57, 0x2c, 0x6a, 0xbf, 0x33, 0x32, 0x32, 0xa6, 0x63, 0xa8, 0xc7, 0x66, 0xea, 0xfd, 0xfb, 0xf7, 0x35, 0x18, 0x18, 0x18, 0x56, 0x31, 0x32, 0x32, 0xea, 0x42, 0x85, 0x6e, 0x30, 0x33, 0x33, 0x87, 0xc9, 0xca, 0xca, 0x5e, 0x26, 0xca, 0x0, 0x6, 0x6, 0x6, 0x86, 0x17, 0x2f, 0x5e, 0x70, 0xff, 0xfc, 0xf9, 0x73, 0xa, 0x3, 0x3, 0x3, 0x3, 0x3b, 0x3b, 0x7b, 0x8e, 0x84, 0x84, 0x4, 0x36, 0x57, 0xd, 0x2, 0x0, 0x0, 0x67, 0xf2, 0x14, 0xc2, 0xc2, 0xbe, 0xf5, 0xb5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x5, 0xa3, 0xe0, 0xc1, 0x7f, 0x54, 0x48, 0x3, 0x5, 0xf, 0xe3, 0x1e, 0x7c, 0x81, 0x4b, 0x7f, 0x7b, 0x98, 0x86, 0xc5, 0x15, 0xf7, 0x35, 0xee, 0x5f, 0x2, 0x4b, 0x5f, 0x7f, 0xac, 0x8b, 0xc3, 0xa1, 0x2f, 0xb8, 0x1f, 0xce, 0x7f, 0x38, 0xff, 0x5, 0x37, 0x75, 0xbd, 0xf, 0x0, 0x52, 0xd4, 0x48, 0xb8, 0x2d, 0x78, 0x5a, 0x91, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char toggle_off_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x7a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x2d, 0x26, 0x2c, 0x4d, 0x2b, 0x37, 0x63, 0x2f, 0x3f, 0x6e, 0x31, 0x43, 0x71, 0x32, 0x44, 0x6c, 0x31, 0x42, 0x51, 0x2c, 0x39, 0x47, 0x2a, 0x35, 0x66, 0x30, 0x40, 0x4d, 0x2b, 0x38, 0x32, 0x26, 0x2e, 0x26, 0x25, 0x2a, 0x2e, 0x25, 0x2c, 0x3c, 0x28, 0x31, 0x52, 0x2c, 0x39, 0x68, 0x30, 0x40, 0x27, 0x25, 0x2a, 0x50, 0x2c, 0x38, 0x5f, 0x2e, 0x3d, 0x35, 0x27, 0x2f, 0x38, 0x27, 0x30, 0x5e, 0x2e, 0x3d, 0x43, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x44, 0x2a, 0x34, 0x2b, 0x26, 0x2c, 0x64, 0x2f, 0x3f, 0x36, 0x27, 0x30, 0x37, 0x27, 0x30, 0x66, 0x2f, 0x40, 0x2c, 0x26, 0x2c, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x97, 0xb0, 0x86, 0xb4, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0x8, 0x17, 0x35, 0x86, 0xf3, 0x7, 0x3a, 0xb4, 0xb9, 0xb, 0x28, 0x8a, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xe6, 0x37, 0xf, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xe5, 0x7c, 0xf3, 0x66, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6b, 0x52, 0x65, 0xa5, 0x98, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x2, 0x47, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0xfb, 0x5b, 0x93, 0x50, 0x18, 0x96, 0x3, 0xc, 0x48, 0x60, 0xde, 0x70, 0x9b, 0x43, 0x37, 0x75, 0xa6, 0x9b, 0xb5, 0xad, 0x56, 0x59, 0x99, 0xe9, 0xc6, 0x39, 0xb3, 0x56, 0x59, 0xcd, 0x4c, 0xbb, 0xd9, 0x3d, 0xdb, 0xd, 0x74, 0xa6, 0xa3, 0xdb, 0xff, 0x1e, 0x70, 0x68, 0x6e, 0xc0, 0xe0, 0xe9, 0xe9, 0x97, 0x7a, 0x79, 0x9e, 0x3, 0xf, 0x7c, 0xdf, 0xcb, 0x7b, 0xbe, 0xf3, 0x5d, 0x86, 0x86, 0xfe, 0x21, 0x10, 0x80, 0x24, 0xa9, 0x40, 0x90, 0x24, 0x20, 0x3c, 0xdd, 0x1, 0x45, 0x87, 0x18, 0x96, 0x3b, 0x17, 0x0, 0x8e, 0x65, 0x42, 0x34, 0x5, 0xdc, 0x7f, 0xa7, 0x86, 0x59, 0x5e, 0x10, 0xc3, 0x23, 0xa3, 0x63, 0xbe, 0x18, 0x1d, 0x9, 0x8b, 0x2, 0xcf, 0xe, 0x53, 0xe, 0x15, 0x60, 0x7c, 0x42, 0x9a, 0x8c, 0x44, 0x63, 0x53, 0xc5, 0x52, 0x0, 0x8a, 0x53, 0xb1, 0x68, 0x64, 0x52, 0x9a, 0x18, 0xef, 0x13, 0x1, 0xe2, 0xf2, 0xf4, 0x4c, 0x42, 0x81, 0x10, 0x21, 0x84, 0xed, 0x90, 0x9, 0x88, 0xca, 0xc8, 0x93, 0x24, 0x31, 0x33, 0x2d, 0xc7, 0x7b, 0x18, 0x88, 0x24, 0x37, 0x3b, 0xa7, 0xc0, 0xcd, 0x3b, 0x77, 0x2b, 0xf7, 0x30, 0xee, 0x3f, 0xd8, 0xb4, 0x58, 0xca, 0x70, 0x80, 0x8e, 0xb9, 0x59, 0x2e, 0x49, 0x9c, 0xed, 0x9f, 0x11, 0xe6, 0x15, 0xb4, 0xf5, 0xf0, 0xd1, 0xe3, 0xaa, 0xf5, 0xb9, 0xba, 0xfd, 0x64, 0xe7, 0xe9, 0xd6, 0xae, 0xa5, 0x64, 0xd0, 0x4e, 0xe6, 0x5, 0xa6, 0x1b, 0x7, 0x90, 0x5a, 0x38, 0xaf, 0xa0, 0xbd, 0x67, 0xcf, 0x7b, 0x2d, 0x5e, 0xbc, 0xdc, 0xdb, 0xf5, 0x8d, 0xc5, 0xe2, 0x42, 0xea, 0xf7, 0x26, 0x28, 0x59, 0x5c, 0x82, 0xaf, 0xf6, 0xb7, 0xfb, 0x2d, 0x5e, 0xef, 0xbf, 0xa9, 0xfa, 0x31, 0x2c, 0xa5, 0xe5, 0xc, 0x96, 0x40, 0xd0, 0xfc, 0x32, 0x7c, 0xfb, 0xee, 0xbd, 0xd3, 0xe2, 0xc3, 0xc7, 0x4f, 0xbe, 0xe7, 0xb1, 0xcc, 0xd3, 0x98, 0x0, 0x84, 0x84, 0xb, 0xf0, 0x60, 0xc7, 0x6d, 0xf1, 0xf9, 0xc0, 0x7e, 0xa8, 0xd5, 0x1b, 0xcd, 0x56, 0x49, 0x6d, 0x18, 0xb0, 0x16, 0xcd, 0x7a, 0x7b, 0x31, 0x9b, 0xc3, 0x7b, 0x20, 0x19, 0x31, 0x86, 0xe, 0x8f, 0xdc, 0x4, 0x47, 0x87, 0xf6, 0x43, 0xfb, 0xb8, 0xf6, 0xa5, 0x51, 0x53, 0x4d, 0x47, 0xd5, 0xf6, 0x36, 0x90, 0x4f, 0x33, 0x24, 0x26, 0x60, 0x2f, 0x5d, 0x46, 0x27, 0xa7, 0x6e, 0x82, 0xd3, 0x13, 0x5b, 0x80, 0xe9, 0x74, 0xdc, 0x51, 0x75, 0x93, 0x40, 0xef, 0x12, 0x14, 0xb, 0x2c, 0x85, 0x9, 0xe4, 0x2b, 0xa, 0xaa, 0x78, 0x4, 0xac, 0x5a, 0xc1, 0x77, 0x55, 0x37, 0x96, 0xaf, 0x75, 0xb5, 0xdd, 0x6e, 0x6b, 0xd6, 0x62, 0x7f, 0xbf, 0x2a, 0xdb, 0x4, 0x6c, 0x58, 0xf1, 0x55, 0xa0, 0x99, 0xa, 0xea, 0xdf, 0x54, 0xd3, 0x53, 0x6d, 0x7b, 0x28, 0x60, 0xc4, 0x6b, 0xd0, 0x37, 0x6, 0x7a, 0xa7, 0xa4, 0x35, 0x5b, 0x4e, 0x82, 0x6e, 0xc, 0x40, 0x28, 0x1b, 0x70, 0xa, 0x6a, 0x53, 0x6f, 0xd6, 0x4b, 0x4e, 0x82, 0xee, 0x29, 0x18, 0x79, 0xb0, 0x12, 0x90, 0x7, 0x5a, 0xe7, 0xbb, 0x11, 0xcb, 0x96, 0x66, 0x2f, 0x18, 0x2b, 0x92, 0x9d, 0x7, 0x66, 0x26, 0x5e, 0x47, 0x7f, 0x91, 0x89, 0x66, 0x2d, 0x2c, 0xfe, 0x79, 0x2d, 0xdc, 0x38, 0xab, 0x5, 0xef, 0x6a, 0xfc, 0xe1, 0xef, 0xdf, 0x5b, 0x8d, 0x66, 0x3f, 0xb8, 0xb9, 0xaa, 0xc0, 0x9f, 0xce, 0x7e, 0xe0, 0xe3, 0xbf, 0x7a, 0xab, 0xa7, 0x1f, 0x98, 0x1d, 0x89, 0x13, 0xd6, 0x6e, 0xc3, 0x32, 0xc4, 0xd, 0x9, 0xd9, 0x80, 0xc6, 0xe5, 0xe9, 0x9f, 0x58, 0x13, 0xb8, 0x78, 0x7f, 0x4f, 0x4b, 0x32, 0x92, 0x18, 0x89, 0xe6, 0x3, 0x5b, 0xa2, 0xf1, 0xf3, 0x7c, 0x34, 0x22, 0x4a, 0x4c, 0xd2, 0xd1, 0x98, 0x9, 0x2a, 0xc5, 0xf2, 0xd9, 0x74, 0x61, 0x7d, 0x2c, 0x0, 0xeb, 0x85, 0x74, 0x96, 0x67, 0x53, 0x94, 0x6b, 0x36, 0x10, 0x20, 0x43, 0xe7, 0x36, 0xe4, 0xe0, 0xb9, 0x20, 0x6f, 0xe4, 0xe8, 0x8c, 0xf7, 0x6c, 0x31, 0x26, 0x53, 0xf0, 0x60, 0x32, 0x46, 0xd3, 0x80, 0xc9, 0xf4, 0xff, 0xe2, 0x17, 0x82, 0xde, 0x40, 0xde, 0x2d, 0xc3, 0x2a, 0xca, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x7a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x14, 0x17, 0x20, 0x20, 0x25, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x10, 0x13, 0x22, 0x22, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19, 0x19, 0x1c, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x12, 0x12, 0x14, 0x23, 0x23, 0x27, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x15, 0x18, 0x20, 0x20, 0x25, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x24, 0x24, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x27, 0x15, 0x15, 0x18, 0x23, 0x23, 0x28, 0x12, 0x12, 0x14, 0x0, 0x0, 0x0, 0x1a, 0x1a, 0x1e, 0x0, 0x0, 0x0, 0x11, 0x11, 0x13, 0x22, 0x22, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x24, 0x29, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x28, 0x25, 0x25, 0x28, 0x25, 0x25, 0x29, 0x25, 0x25, 0x27, 0x2d, 0x26, 0x2c, 0x4d, 0x2b, 0x37, 0x63, 0x2f, 0x3f, 0x6e, 0x31, 0x43, 0x71, 0x32, 0x44, 0x6c, 0x31, 0x42, 0x51, 0x2c, 0x39, 0x47, 0x2a, 0x35, 0x66, 0x30, 0x40, 0x4d, 0x2b, 0x38, 0x32, 0x26, 0x2e, 0x26, 0x25, 0x2a, 0x2e, 0x25, 0x2c, 0x3c, 0x28, 0x31, 0x52, 0x2c, 0x39, 0x68, 0x30, 0x40, 0x27, 0x25, 0x2a, 0x50, 0x2c, 0x38, 0x5f, 0x2e, 0x3d, 0x35, 0x27, 0x2f, 0x38, 0x27, 0x30, 0x5e, 0x2e, 0x3d, 0x43, 0x2a, 0x34, 0x5f, 0x2f, 0x3e, 0x2f, 0x25, 0x2c, 0x44, 0x2a, 0x34, 0x2b, 0x26, 0x2c, 0x64, 0x2f, 0x3f, 0x36, 0x27, 0x30, 0x37, 0x27, 0x30, 0x66, 0x2f, 0x40, 0x2c, 0x26, 0x2c, 0x46, 0x2a, 0x35, 0x53, 0x2c, 0x39, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x32, 0x32, 0x37, 0x5d, 0x2e, 0x3d, 0x3e, 0x29, 0x32, 0xc9, 0xc9, 0xca, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x69, 0x30, 0x41, 0x2f, 0x26, 0x2d, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0x4e, 0x4e, 0x52, 0x48, 0x2b, 0x36, 0x2c, 0x26, 0x2b, 0x97, 0xb0, 0x86, 0xb4, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x98, 0xe5, 0xfa, 0xfe, 0x8, 0x17, 0x35, 0x86, 0xf3, 0x7, 0x3a, 0xb4, 0xb9, 0xb, 0x28, 0x8a, 0x8b, 0xf6, 0x45, 0x5, 0x9b, 0xe6, 0xe6, 0x37, 0xf, 0xfb, 0x4c, 0xfe, 0x4e, 0x4f, 0x50, 0xfb, 0x9c, 0xf6, 0x8c, 0x3b, 0xbb, 0x3c, 0x87, 0xf3, 0x53, 0x14, 0xe5, 0x7c, 0xf3, 0x66, 0x0, 0x0, 0x2, 0x29, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x3, 0x93, 0x24, 0x4b, 0x14, 0x85, 0x5f, 0xb9, 0xaa, 0x6d, 0x8e, 0x6d, 0xdb, 0x73, 0x2b, 0xb3, 0xe7, 0xad, 0x6d, 0xab, 0xdd, 0x63, 0xe3, 0xbf, 0x6f, 0x67, 0x65, 0xbb, 0xb4, 0xbb, 0x81, 0xc5, 0x97, 0x11, 0x27, 0x78, 0x4f, 0xea, 0xe2, 0xbf, 0x3f, 0x9, 0x86, 0xe5, 0x38, 0xde, 0x16, 0x8e, 0x63, 0x19, 0xc3, 0x70, 0x96, 0x17, 0x44, 0x49, 0x56, 0x1c, 0x36, 0x28, 0xb2, 0x24, 0xa, 0x3c, 0xab, 0xdf, 0x9d, 0x77, 0xca, 0x2e, 0xb7, 0xc7, 0xeb, 0xf3, 0x7, 0x2c, 0xf1, 0xfb, 0xbc, 0x1e, 0xb7, 0x4b, 0x76, 0xf2, 0x2d, 0xa7, 0x60, 0x83, 0xa1, 0x70, 0x24, 0x1a, 0x8b, 0x27, 0xb6, 0xc1, 0x86, 0xed, 0x44, 0x3c, 0x16, 0x8d, 0x84, 0x43, 0x41, 0xb6, 0x29, 0x3e, 0xd9, 0xd6, 0xde, 0xd1, 0xa9, 0x22, 0x84, 0x31, 0x6, 0xd, 0x4c, 0x40, 0x38, 0x85, 0xd, 0x4d, 0x3a, 0x3b, 0xda, 0xdb, 0x92, 0xd, 0xe, 0x4c, 0x97, 0xd2, 0xdd, 0xa3, 0xa2, 0x9d, 0xff, 0x6f, 0xdd, 0xbe, 0x43, 0xb9, 0x7b, 0x6f, 0x47, 0x73, 0x49, 0x21, 0x93, 0x73, 0xf4, 0x74, 0x2b, 0x5d, 0x4c, 0xfd, 0xfe, 0x92, 0xbb, 0x57, 0xc5, 0xf7, 0x1f, 0x3c, 0x7c, 0xf4, 0x18, 0x8, 0x8f, 0x9f, 0x3c, 0x7d, 0xf6, 0xfc, 0xfe, 0xb, 0xed, 0x24, 0x66, 0x37, 0xe9, 0x75, 0x4b, 0xb5, 0x77, 0x60, 0xfb, 0xfa, 0x7, 0x54, 0xfc, 0xf2, 0xd5, 0x6b, 0x68, 0xe0, 0xcd, 0xdb, 0x97, 0x2f, 0x2c, 0xdf, 0x62, 0xb0, 0xbf, 0xaf, 0x7a, 0x9, 0xbe, 0xcd, 0x33, 0x84, 0xde, 0xbd, 0x7f, 0x2, 0x4d, 0x7c, 0x78, 0xff, 0xf1, 0x31, 0x58, 0x30, 0x34, 0xdc, 0x36, 0x42, 0x8f, 0xc0, 0x8, 0xae, 0x51, 0xf4, 0xe9, 0xf3, 0x17, 0x68, 0xe1, 0xeb, 0xb7, 0x34, 0x58, 0x31, 0xea, 0x12, 0xa8, 0x1, 0x2b, 0xba, 0xc7, 0x50, 0xe6, 0x19, 0xe8, 0xc8, 0x66, 0x80, 0x92, 0xcb, 0x17, 0x8a, 0x25, 0xd8, 0x2d, 0x94, 0xd1, 0x64, 0xf, 0x8, 0xe3, 0x13, 0x93, 0xf4, 0xe, 0x9c, 0xe4, 0x89, 0xe3, 0xfd, 0x3, 0xd0, 0x71, 0xb0, 0xf, 0x94, 0xc3, 0xa3, 0xdc, 0x71, 0x21, 0xb7, 0x4b, 0x2, 0x35, 0xa1, 0x4c, 0xd, 0x4b, 0x1c, 0x35, 0x90, 0xa7, 0x67, 0xf0, 0xc9, 0x29, 0xe8, 0x38, 0x3d, 0xa9, 0x1c, 0x80, 0x4, 0x1d, 0x9d, 0xed, 0x9e, 0x13, 0x3, 0x22, 0x94, 0xed, 0x59, 0x99, 0xa7, 0x6, 0x6d, 0x73, 0x2a, 0xbe, 0x6d, 0xf0, 0x60, 0x8f, 0x6f, 0x13, 0x25, 0x41, 0x65, 0xb9, 0xc8, 0xef, 0x1e, 0x1e, 0x1e, 0xee, 0x69, 0x2, 0x94, 0xf9, 0xb6, 0x8a, 0x81, 0xec, 0x55, 0x2d, 0x4f, 0xb0, 0x47, 0x4e, 0x90, 0xbf, 0xdc, 0x25, 0x91, 0x44, 0x74, 0x27, 0x90, 0x3c, 0xb, 0xc8, 0xf2, 0xd, 0xce, 0xcf, 0x60, 0xaf, 0x58, 0xaa, 0x18, 0xe8, 0xdf, 0x80, 0x15, 0x27, 0x6c, 0x7e, 0x61, 0xb7, 0x78, 0x5e, 0xcc, 0x43, 0xab, 0x1, 0xfd, 0x5, 0x9a, 0x7, 0x8b, 0x36, 0x79, 0xb0, 0x77, 0x76, 0x5, 0x90, 0x2b, 0xed, 0x55, 0x84, 0xb2, 0x18, 0x16, 0x98, 0x5a, 0x26, 0x2e, 0xe1, 0x5f, 0xce, 0x44, 0x5a, 0xb, 0x83, 0x3f, 0x5f, 0xb, 0xcb, 0xb4, 0x16, 0xcc, 0xab, 0xf1, 0x9a, 0xc6, 0xdb, 0x57, 0x23, 0xed, 0x7, 0x2b, 0xab, 0x2a, 0xba, 0x69, 0xed, 0x7, 0xe6, 0x6c, 0xaf, 0xae, 0xd5, 0xfa, 0x1, 0xed, 0x48, 0x8a, 0x7b, 0x7d, 0x3, 0xa5, 0x10, 0x6d, 0x48, 0xb8, 0x2, 0x2a, 0x2f, 0x30, 0xa2, 0x73, 0xdd, 0xad, 0x24, 0x9b, 0x7b, 0x5a, 0x97, 0x14, 0xf6, 0x44, 0x63, 0x53, 0xdb, 0x60, 0xcb, 0xf6, 0x54, 0x2c, 0xea, 0x9, 0x4b, 0x5d, 0x6c, 0x6b, 0x57, 0xee, 0x93, 0x5d, 0x13, 0xc3, 0xb3, 0x9b, 0x1, 0x1b, 0x36, 0x67, 0x87, 0x27, 0x5c, 0x72, 0x1f, 0xcf, 0xe8, 0xa7, 0xca, 0x88, 0x30, 0xb9, 0xd5, 0x66, 0x3f, 0x17, 0xda, 0xb6, 0x26, 0x85, 0x11, 0x96, 0x31, 0x99, 0x4c, 0xfc, 0xf, 0x40, 0x27, 0xd3, 0xbf, 0xc4, 0x77, 0x82, 0xde, 0x40, 0xde, 0x4b, 0x3f, 0xe2, 0x98, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char toggle_on_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x65, 0xb5, 0xdd, 0x88, 0x9f, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x2, 0x28, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xd5, 0x55, 0x69, 0x57, 0xd3, 0x40, 0x14, 0x25, 0x93, 0xa4, 0x49, 0x24, 0x49, 0xd9, 0x42, 0xd7, 0x0, 0x85, 0x50, 0x2c, 0xb4, 0x68, 0x5b, 0xab, 0x28, 0xb2, 0xc, 0x4b, 0x95, 0x82, 0x56, 0xab, 0x42, 0x45, 0x5c, 0x9b, 0xad, 0x45, 0x11, 0x6d, 0x3, 0xa8, 0x7f, 0xde, 0xc4, 0x94, 0x26, 0x69, 0x30, 0x89, 0xc7, 0x2f, 0x7a, 0x3f, 0xe4, 0x9c, 0xcc, 0x3b, 0xef, 0xce, 0x7d, 0x6f, 0xde, 0x32, 0x30, 0xf0, 0xf, 0x1, 0x1, 0x28, 0x8a, 0xf9, 0x2, 0x45, 0x1, 0x72, 0xa5, 0x3b, 0xc0, 0xf0, 0x10, 0x41, 0x52, 0xd7, 0x7c, 0x40, 0x91, 0x44, 0x8, 0xc7, 0x80, 0xfb, 0x76, 0x6c, 0x90, 0xa4, 0x19, 0x36, 0x3c, 0x34, 0x3c, 0xe2, 0x89, 0xe1, 0xa1, 0x30, 0xcb, 0xd0, 0xe4, 0x20, 0xd6, 0xa7, 0x2, 0x8c, 0x8e, 0x71, 0xe3, 0x91, 0x68, 0x2c, 0x9e, 0x80, 0x3e, 0x48, 0xc4, 0x63, 0xd1, 0xc8, 0x38, 0x37, 0x36, 0xea, 0x10, 0x1, 0x92, 0xfc, 0xc4, 0xe4, 0xd4, 0x86, 0x9f, 0xf3, 0x25, 0x36, 0xa6, 0x26, 0x27, 0xf8, 0xa4, 0x8d, 0x1, 0x49, 0x51, 0xd3, 0x33, 0x82, 0xa7, 0xcf, 0xe6, 0xd6, 0x76, 0xf9, 0x81, 0x89, 0x87, 0x3b, 0x9b, 0x50, 0x98, 0x99, 0xa6, 0x52, 0x88, 0x15, 0x3f, 0xc1, 0xcc, 0x7a, 0x6a, 0xaf, 0xec, 0xee, 0x3d, 0x7a, 0x5c, 0x7d, 0x62, 0xe0, 0x69, 0xed, 0xd9, 0xf3, 0x17, 0xfb, 0x95, 0xc4, 0x2c, 0x43, 0xf4, 0xf2, 0x0, 0xd2, 0x73, 0xd7, 0x33, 0x9e, 0xfe, 0x7, 0xf5, 0x97, 0xf6, 0xff, 0xc3, 0x57, 0x7, 0x95, 0xcc, 0xfc, 0x5c, 0xfa, 0x32, 0x8, 0x8c, 0x67, 0x17, 0xbc, 0xfc, 0x8f, 0x5e, 0xd7, 0x6b, 0xce, 0x93, 0x5a, 0xfd, 0xcd, 0xd1, 0x42, 0x96, 0xcf, 0x99, 0x12, 0x10, 0x9c, 0x5e, 0xf4, 0x8c, 0xff, 0xed, 0xbb, 0xf7, 0xfd, 0x47, 0x87, 0x7b, 0x1f, 0xe0, 0x22, 0x8d, 0x9b, 0x4, 0x20, 0xc4, 0xdc, 0xb0, 0x4c, 0xd, 0x51, 0x92, 0x15, 0xa8, 0x4a, 0x4d, 0xa8, 0xb6, 0xba, 0x47, 0xc7, 0x1f, 0xdd, 0xa4, 0x9f, 0x8e, 0xe1, 0xcd, 0x7c, 0xc1, 0x8c, 0x1, 0x25, 0xd8, 0x98, 0x65, 0x69, 0x9d, 0x34, 0x3e, 0x4b, 0xd, 0x55, 0x6a, 0x59, 0x4, 0xa7, 0x5f, 0xdc, 0x4, 0x5f, 0x4f, 0x61, 0x31, 0x4b, 0xa0, 0x26, 0x1, 0x79, 0x2b, 0x6e, 0x9, 0x90, 0xda, 0x10, 0x9e, 0x74, 0x54, 0x4d, 0x56, 0x7b, 0x4, 0x67, 0xe7, 0x6e, 0x82, 0xf3, 0x33, 0x28, 0x94, 0x48, 0xcc, 0x24, 0xe0, 0x6f, 0x5b, 0x6f, 0xa8, 0x6a, 0xfa, 0xe7, 0x42, 0x54, 0x5b, 0x17, 0x5a, 0x8f, 0xa0, 0x5c, 0x75, 0x13, 0x54, 0xcb, 0x10, 0xde, 0xe1, 0xbb, 0x4, 0x64, 0xd8, 0x2a, 0xa2, 0xb6, 0xa1, 0x40, 0xfc, 0xa6, 0x3b, 0x7f, 0x17, 0x3, 0x2b, 0x20, 0xd8, 0xa2, 0x65, 0xd1, 0x3a, 0xb0, 0x2d, 0x2b, 0x3a, 0x81, 0x22, 0x5, 0xcd, 0x1, 0x8, 0xe5, 0x6d, 0xaf, 0xa0, 0xca, 0x9a, 0x2c, 0xfe, 0x4a, 0xa0, 0x16, 0xf4, 0x15, 0xf4, 0x3a, 0x58, 0xb2, 0x99, 0xda, 0x9d, 0xa6, 0x9e, 0x4b, 0x5, 0xc2, 0xa6, 0xe2, 0x5d, 0x7, 0x4b, 0x5c, 0xb7, 0xe, 0xfe, 0xba, 0x12, 0x8d, 0x5e, 0x98, 0xff, 0xf3, 0x5e, 0xb8, 0x6b, 0xf5, 0x42, 0x80, 0x6e, 0xdc, 0x77, 0x76, 0xe3, 0xf, 0x67, 0x37, 0x1a, 0xf3, 0xe0, 0xde, 0xb2, 0xa7, 0x86, 0xfe, 0x79, 0x90, 0x59, 0xbe, 0x6f, 0x9b, 0x7, 0xc6, 0x44, 0xa2, 0x98, 0x95, 0x55, 0x18, 0x18, 0xab, 0x2b, 0xc, 0x95, 0x74, 0xce, 0xb4, 0x14, 0xc1, 0xb1, 0x91, 0x68, 0x51, 0xf0, 0x77, 0x16, 0x8a, 0xd1, 0x8, 0xcb, 0x11, 0xa9, 0xbe, 0xc1, 0x8c, 0x60, 0x69, 0x92, 0xce, 0x67, 0x4b, 0x6b, 0x23, 0x3e, 0x58, 0x2b, 0x65, 0xf3, 0x34, 0x99, 0xc6, 0x5c, 0xbb, 0x1, 0x1, 0x39, 0xbc, 0xb0, 0xce, 0xfb, 0xef, 0x5, 0x7e, 0xbd, 0x80, 0xe7, 0xae, 0xde, 0x2d, 0xfa, 0x66, 0xf2, 0x5f, 0x4c, 0xfa, 0x6a, 0xfa, 0xcd, 0x66, 0xfa, 0x7f, 0xf1, 0x13, 0xb7, 0x71, 0x36, 0xc6, 0x87, 0x41, 0xd4, 0x5, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x20, 0x8, 0x3, 0x0, 0x0, 0x0, 0x95, 0x43, 0x8e, 0xb6, 0x0, 0x0, 0x1, 0x74, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xd, 0xf, 0x1a, 0x1a, 0x1e, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x24, 0x24, 0x29, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0xa, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x11, 0x14, 0x23, 0x23, 0x28, 0x12, 0x12, 0x15, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xb, 0xd, 0x23, 0x23, 0x28, 0xb, 0xb, 0xd, 0x1e, 0x1e, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0x1a, 0x1a, 0x1e, 0x1a, 0x1a, 0x1d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xe, 0x10, 0xb, 0xb, 0xd, 0x0, 0x0, 0x0, 0x13, 0x13, 0x15, 0x0, 0x0, 0x0, 0xb, 0xb, 0xc, 0x1d, 0x1d, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x25, 0x25, 0x2a, 0x24, 0x24, 0x29, 0x25, 0x2c, 0x36, 0x27, 0x49, 0x65, 0x29, 0x5d, 0x85, 0x2a, 0x66, 0x95, 0x2a, 0x68, 0x99, 0x29, 0x64, 0x92, 0x28, 0x4c, 0x6b, 0x25, 0x27, 0x2d, 0x27, 0x43, 0x5c, 0x29, 0x5f, 0x89, 0x27, 0x49, 0x66, 0x25, 0x30, 0x3e, 0x25, 0x26, 0x2d, 0x25, 0x25, 0x2b, 0x25, 0x26, 0x2c, 0x25, 0x2d, 0x38, 0x25, 0x3a, 0x4c, 0x27, 0x4d, 0x6b, 0x29, 0x60, 0x8c, 0x27, 0x44, 0x5c, 0x27, 0x4b, 0x69, 0x28, 0x59, 0x7f, 0x25, 0x34, 0x43, 0x25, 0x35, 0x45, 0x28, 0x58, 0x7f, 0x25, 0x26, 0x2b, 0x27, 0x40, 0x57, 0x27, 0x41, 0x57, 0x25, 0x2a, 0x33, 0x29, 0x5d, 0x87, 0x25, 0x34, 0x44, 0x25, 0x2b, 0x34, 0x40, 0x40, 0x44, 0xad, 0xad, 0xaf, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0x77, 0x77, 0x7a, 0x5b, 0x5b, 0x5f, 0x4e, 0x4e, 0x52, 0xc9, 0xc9, 0xca, 0x27, 0x43, 0x5b, 0x27, 0x4d, 0x6c, 0x27, 0x4e, 0x6d, 0xbb, 0xbb, 0xbd, 0x69, 0x69, 0x6c, 0x28, 0x56, 0x7b, 0x26, 0x3b, 0x4e, 0x26, 0x3a, 0x4e, 0x32, 0x32, 0x37, 0x84, 0x84, 0x87, 0xd6, 0xd6, 0xd7, 0x29, 0x61, 0x8d, 0x25, 0x2e, 0x39, 0x92, 0x92, 0x94, 0xa0, 0xa0, 0xa2, 0xe4, 0xe4, 0xe5, 0x27, 0x44, 0x5d, 0xdd, 0xc9, 0xf2, 0x7e, 0x0, 0x0, 0x0, 0x41, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9, 0xe, 0x13, 0x16, 0x18, 0x19, 0xa, 0x26, 0x36, 0x44, 0x4d, 0x52, 0x54, 0x55, 0x6, 0x12, 0x27, 0x43, 0x80, 0xc5, 0xe7, 0xf5, 0xfe, 0x8, 0x17, 0x35, 0x73, 0xd9, 0x7, 0x3a, 0x96, 0xf9, 0x9a, 0xb, 0x28, 0x76, 0xfb, 0x77, 0xde, 0x45, 0x5, 0x82, 0xc6, 0xc6, 0x37, 0xf, 0xe9, 0x4c, 0x4e, 0x4f, 0x50, 0x83, 0x78, 0x3b, 0x9c, 0x3c, 0x74, 0xda, 0x53, 0x14, 0x37, 0x21, 0x5a, 0x6c, 0x0, 0x0, 0x2, 0x4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xdd, 0x95, 0x63, 0x83, 0xdc, 0x60, 0x10, 0xc7, 0x1b, 0x3c, 0xc1, 0xda, 0x3e, 0xdb, 0x36, 0xe7, 0x6c, 0xdb, 0xa, 0xcf, 0xc6, 0x7e, 0xf8, 0x2a, 0x6d, 0xb8, 0xac, 0x7b, 0xbf, 0xf7, 0xf3, 0x1f, 0xcf, 0x7c, 0xf8, 0x97, 0xc0, 0x70, 0x82, 0x20, 0xb3, 0x42, 0x10, 0x38, 0x96, 0xd2, 0x1c, 0x27, 0x11, 0x45, 0x33, 0xac, 0x2d, 0xb, 0x2c, 0x43, 0x53, 0x88, 0xc4, 0xad, 0xde, 0x49, 0x3b, 0xe3, 0x70, 0xba, 0xdc, 0x1e, 0xaf, 0x2f, 0x23, 0x5e, 0x8f, 0xdb, 0xe5, 0x74, 0x30, 0x76, 0xd2, 0x14, 0x5, 0xee, 0xf, 0x4, 0x43, 0xe1, 0x48, 0x34, 0x16, 0x87, 0x2c, 0xc4, 0x63, 0xd1, 0x48, 0x38, 0x14, 0xc, 0xf8, 0x71, 0x83, 0x7d, 0xa2, 0xa0, 0xb0, 0xa8, 0x78, 0x4, 0x72, 0x64, 0xa4, 0xb8, 0xa8, 0xb0, 0x20, 0xa1, 0x53, 0xc0, 0x4a, 0xd8, 0xd2, 0xb2, 0x72, 0xc8, 0xc4, 0xe8, 0xd8, 0xf8, 0xc4, 0xa4, 0xc2, 0xd4, 0xf4, 0x28, 0x94, 0x97, 0x95, 0xb2, 0x25, 0x98, 0x96, 0x3f, 0xed, 0xac, 0xc8, 0x18, 0xfb, 0xcc, 0xec, 0xdc, 0xfc, 0xc2, 0xe2, 0xd2, 0x17, 0x96, 0x57, 0x56, 0xd7, 0xd6, 0x37, 0x66, 0xe2, 0x15, 0x4e, 0x5a, 0xad, 0x3, 0x5e, 0x59, 0x55, 0x5d, 0x93, 0xd1, 0x7e, 0x73, 0x6b, 0x1b, 0x74, 0xec, 0xec, 0x6e, 0xce, 0xd4, 0xd4, 0x56, 0x55, 0x7e, 0x4f, 0x82, 0x2c, 0x70, 0xd5, 0x41, 0x6, 0xf6, 0xf6, 0xb7, 0x56, 0xc0, 0xc0, 0xca, 0xd6, 0xc1, 0x5e, 0x5d, 0x7d, 0x41, 0x83, 0x12, 0x2, 0x86, 0x1c, 0x8d, 0x90, 0x89, 0xc3, 0xa3, 0x63, 0x30, 0xb1, 0x33, 0x77, 0x2, 0x8d, 0xe, 0xa4, 0x8, 0xe0, 0x94, 0xb3, 0x9, 0x54, 0x4e, 0xcf, 0x38, 0x5e, 0x0, 0x91, 0x93, 0x40, 0x94, 0x41, 0xe1, 0xfc, 0x2, 0x2c, 0x5c, 0x9e, 0x43, 0x73, 0x4b, 0xab, 0x92, 0x3, 0x41, 0xbb, 0xa2, 0xa0, 0x22, 0x5f, 0x9d, 0x5e, 0x73, 0xa7, 0x22, 0x27, 0x6b, 0x2, 0x37, 0xb7, 0x60, 0xe1, 0xee, 0x6, 0xda, 0xea, 0x69, 0x42, 0x11, 0x60, 0xda, 0x63, 0x5a, 0x0, 0xdc, 0x3d, 0xc0, 0xd5, 0x83, 0xf8, 0xc8, 0x8b, 0xaa, 0xc0, 0xd3, 0x33, 0x58, 0x78, 0x7e, 0x82, 0xf2, 0xe, 0x86, 0x54, 0x4, 0xa, 0x3a, 0xb5, 0x1e, 0x8a, 0x8f, 0x0, 0xf0, 0x72, 0x26, 0xca, 0x2f, 0x8f, 0xaa, 0xc0, 0xc4, 0x22, 0x58, 0x58, 0x9c, 0x0, 0xe8, 0x2a, 0xf8, 0x26, 0xc0, 0xb8, 0xb5, 0x21, 0xba, 0xff, 0x12, 0xc1, 0xd9, 0xeb, 0x67, 0xe3, 0xb7, 0xb3, 0x9c, 0x23, 0xa0, 0x5d, 0x6d, 0xa0, 0xf2, 0xf8, 0x0, 0xf7, 0xbc, 0xf0, 0x59, 0x40, 0xe0, 0x72, 0xad, 0x1, 0x4e, 0xb5, 0xe8, 0xba, 0x20, 0xf2, 0x8f, 0xfc, 0xd9, 0xd7, 0x2, 0x3e, 0xe6, 0xda, 0x5, 0xc, 0x39, 0xba, 0x41, 0xe3, 0xfe, 0x41, 0x2, 0x38, 0x15, 0x0, 0x24, 0x21, 0xf3, 0x1c, 0x74, 0x7, 0x11, 0xf6, 0xd3, 0x93, 0xa8, 0xee, 0x42, 0x6d, 0xfe, 0xbb, 0xd0, 0xa3, 0xed, 0x42, 0xe, 0xdb, 0xb8, 0x61, 0xdc, 0xc6, 0xa4, 0xba, 0x8d, 0xea, 0x3d, 0xe8, 0xed, 0xab, 0xc9, 0xe7, 0x1e, 0xd4, 0xf4, 0xf5, 0xab, 0xf7, 0x40, 0xb9, 0x48, 0xac, 0x73, 0x60, 0x10, 0x72, 0x66, 0x70, 0xc0, 0xc9, 0x26, 0x8c, 0x37, 0xad, 0x84, 0xe, 0xba, 0xc2, 0x91, 0xb6, 0x72, 0xc8, 0x4a, 0x79, 0x5b, 0x24, 0xec, 0xa, 0xd2, 0x25, 0xb8, 0xf9, 0x2a, 0x57, 0x32, 0x8e, 0x96, 0xfa, 0x8e, 0x21, 0x5f, 0x16, 0x86, 0x3a, 0xea, 0x5b, 0x1c, 0x4c, 0x25, 0x89, 0x59, 0xbf, 0x4a, 0x3, 0x6a, 0x1d, 0x2e, 0xc8, 0xfe, 0x17, 0xa, 0x86, 0x5b, 0x51, 0x3, 0x8e, 0xa5, 0xf9, 0x4c, 0x64, 0xe, 0x68, 0x9f, 0xe9, 0xbd, 0xf0, 0x9, 0xb7, 0x71, 0x36, 0xc6, 0x9b, 0x3d, 0x7f, 0x21, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tool_button_pressed_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x2, 0x1, 0x50, 0x4c, 0x54, 0x45, 0x29, 0x3a, 0x40, 0x2d, 0x3e, 0x44, 0x26, 0x34, 0x3b, 0x24, 0x34, 0x39, 0x23, 0x31, 0x38, 0x22, 0x31, 0x37, 0x22, 0x31, 0x37, 0x22, 0x30, 0x36, 0x22, 0x31, 0x36, 0x26, 0x34, 0x3c, 0x32, 0x44, 0x4c, 0x26, 0x34, 0x39, 0x23, 0x31, 0x36, 0x21, 0x2e, 0x34, 0x1f, 0x2c, 0x30, 0x1f, 0x2b, 0x2f, 0x1f, 0x2a, 0x2e, 0x1e, 0x2b, 0x2f, 0x1f, 0x2b, 0x2e, 0x36, 0x4b, 0x52, 0x25, 0x33, 0x38, 0x20, 0x2f, 0x32, 0x1c, 0x29, 0x2e, 0x1b, 0x26, 0x2a, 0x1a, 0x23, 0x26, 0x18, 0x22, 0x26, 0x19, 0x22, 0x26, 0x19, 0x23, 0x26, 0x19, 0x26, 0x29, 0x20, 0x2d, 0x32, 0x25, 0x31, 0x38, 0x3c, 0x51, 0x59, 0x23, 0x31, 0x37, 0x1f, 0x2b, 0x31, 0x1a, 0x25, 0x2b, 0x17, 0x20, 0x24, 0x15, 0x1c, 0x21, 0x14, 0x1b, 0x21, 0x14, 0x1c, 0x21, 0x13, 0x1b, 0x21, 0x15, 0x1d, 0x21, 0x1a, 0x25, 0x2a, 0x40, 0x57, 0x60, 0x23, 0x31, 0x36, 0x1f, 0x2b, 0x31, 0x1b, 0x25, 0x29, 0x16, 0x1e, 0x23, 0x14, 0x1b, 0x1d, 0x12, 0x19, 0x1d, 0x12, 0x1b, 0x1d, 0x14, 0x1a, 0x1d, 0x45, 0x5e, 0x67, 0x22, 0x32, 0x37, 0x20, 0x2d, 0x31, 0x1a, 0x26, 0x2a, 0x15, 0x1f, 0x25, 0x14, 0x1c, 0x1f, 0x12, 0x1b, 0x1f, 0x12, 0x1b, 0x20, 0x14, 0x1b, 0x1f, 0x15, 0x1e, 0x24, 0x1a, 0x25, 0x29, 0x4b, 0x64, 0x6d, 0x23, 0x32, 0x38, 0x20, 0x2e, 0x32, 0x1b, 0x27, 0x2b, 0x17, 0x22, 0x27, 0x16, 0x1e, 0x23, 0x14, 0x1e, 0x23, 0x16, 0x20, 0x24, 0x14, 0x1e, 0x22, 0x15, 0x1e, 0x22, 0x17, 0x21, 0x27, 0x1c, 0x27, 0x2c, 0x4f, 0x6a, 0x75, 0x21, 0x2f, 0x33, 0x1d, 0x29, 0x2d, 0x19, 0x23, 0x2a, 0x18, 0x22, 0x27, 0x16, 0x21, 0x27, 0x18, 0x23, 0x29, 0x17, 0x21, 0x26, 0x19, 0x23, 0x29, 0x1c, 0x28, 0x2d, 0x21, 0x2e, 0x33, 0x54, 0x70, 0x7c, 0x23, 0x33, 0x38, 0x22, 0x30, 0x34, 0x1e, 0x2a, 0x2f, 0x1a, 0x26, 0x2d, 0x1a, 0x25, 0x2b, 0x19, 0x25, 0x2b, 0x1a, 0x26, 0x2d, 0x1a, 0x26, 0x2c, 0x18, 0x25, 0x2a, 0x1a, 0x24, 0x2a, 0x1a, 0x25, 0x2c, 0x1d, 0x2a, 0x2f, 0x22, 0x2f, 0x34, 0x59, 0x77, 0x82, 0x23, 0x33, 0x39, 0x22, 0x30, 0x35, 0x1f, 0x2c, 0x31, 0x1c, 0x28, 0x30, 0x1c, 0x28, 0x2e, 0x1b, 0x29, 0x2f, 0x1c, 0x2a, 0x31, 0x1b, 0x28, 0x2f, 0x1c, 0x28, 0x2d, 0x1b, 0x27, 0x2f, 0x1f, 0x2b, 0x31, 0x5e, 0x7d, 0x8a, 0x24, 0x34, 0x39, 0x21, 0x2f, 0x37, 0x20, 0x2d, 0x34, 0x1d, 0x2b, 0x33, 0x1d, 0x2b, 0x32, 0x1d, 0x2d, 0x35, 0x1e, 0x2e, 0x36, 0x1f, 0x2e, 0x36, 0x1d, 0x2b, 0x34, 0x1d, 0x2b, 0x31, 0x1d, 0x2b, 0x32, 0x20, 0x2d, 0x32, 0x21, 0x2f, 0x36, 0x63, 0x83, 0x90, 0x25, 0x34, 0x39, 0x21, 0x31, 0x36, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x36, 0x20, 0x31, 0x39, 0x21, 0x33, 0x3b, 0x21, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1e, 0x2d, 0x33, 0x1f, 0x2d, 0x33, 0x21, 0x30, 0x36, 0x67, 0x8a, 0x97, 0x24, 0x33, 0x39, 0x20, 0x30, 0x36, 0x1f, 0x2f, 0x35, 0x21, 0x30, 0x37, 0x22, 0x32, 0x39, 0x21, 0x35, 0x3e, 0x24, 0x37, 0x41, 0x24, 0x36, 0x41, 0x21, 0x33, 0x3c, 0x21, 0x31, 0x38, 0x1e, 0x2f, 0x35, 0x1e, 0x2e, 0x35, 0x20, 0x2e, 0x35, 0x24, 0x31, 0x39, 0x6c, 0x90, 0x9e, 0x22, 0x30, 0x36, 0x1f, 0x2e, 0x36, 0x20, 0x30, 0x36, 0x20, 0x31, 0x39, 0x23, 0x34, 0x3d, 0x23, 0x37, 0x41, 0x26, 0x3c, 0x47, 0x26, 0x3b, 0x46, 0x22, 0x35, 0x3f, 0x22, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1f, 0x2d, 0x35, 0x21, 0x30, 0x36, 0x72, 0x96, 0xa5, 0x72, 0x96, 0xa5, 0xe8, 0x14, 0xaa, 0x3f, 0x0, 0x0, 0x0, 0xaa, 0x74, 0x52, 0x4e, 0x53, 0xc3, 0xc3, 0xe6, 0xd7, 0xcb, 0xc3, 0xbf, 0xbe, 0xbd, 0xe5, 0xc3, 0xd7, 0xc0, 0xac, 0xa0, 0x9a, 0x98, 0x98, 0x98, 0xc3, 0xcb, 0xac, 0x92, 0x82, 0x7b, 0x78, 0x78, 0x7b, 0x82, 0xac, 0xcb, 0xc3, 0xc3, 0xa0, 0x82, 0x6f, 0x67, 0x64, 0x63, 0x64, 0x67, 0x82, 0xc3, 0xbf, 0x9a, 0x7a, 0x67, 0x5e, 0x5b, 0x5a, 0x5e, 0xc3, 0xbd, 0x98, 0x78, 0x64, 0x5b, 0x57, 0x57, 0x5b, 0x64, 0x78, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x77, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0xbb, 0x96, 0x76, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x76, 0x96, 0xc3, 0xb5, 0x92, 0x75, 0x62, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x59, 0x62, 0x74, 0x92, 0xc3, 0xa9, 0x8b, 0x71, 0x61, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x61, 0x71, 0x8b, 0xa9, 0xc3, 0x95, 0x7e, 0x6b, 0x5e, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x5e, 0x6b, 0x7e, 0x95, 0xc3, 0x4f, 0x78, 0x99, 0x30, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xa9, 0x27, 0xf, 0x6, 0x4, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xff, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0x40, 0x7, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x1c, 0x1c, 0x6c, 0xac, 0x2c, 0xcc, 0x9c, 0x8c, 0xc, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x2, 0x82, 0x82, 0x42, 0xfc, 0x7c, 0xbc, 0x3c, 0xdc, 0x5c, 0xc, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0x52, 0x92, 0xd2, 0x32, 0x62, 0xb2, 0x72, 0xc2, 0xc, 0xf2, 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0x6a, 0xea, 0x1a, 0xca, 0x9a, 0x8a, 0xa, 0xf2, 0xc, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x6, 0x86, 0x86, 0x6, 0x46, 0x7a, 0xba, 0x3a, 0xda, 0x5a, 0xc, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0x56, 0x96, 0xd6, 0x36, 0xb6, 0xa6, 0x26, 0xc6, 0xc, 0x76, 0xf6, 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0xe, 0x26, 0x76, 0xc, 0x5e, 0xf6, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x1, 0x1, 0xfe, 0x81, 0x41, 0xc1, 0x21, 0xf6, 0x5e, 0xc, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x9, 0x89, 0x49, 0x61, 0xa1, 0xc, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xa9, 0x29, 0xc9, 0xc, 0xf9, 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0x5, 0xf9, 0xc, 0xb5, 0x75, 0xf5, 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0x75, 0xb5, 0xc, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x13, 0x26, 0x4e, 0x9a, 0x3c, 0x65, 0xea, 0xb4, 0xe9, 0x33, 0x66, 0x76, 0x33, 0xcc, 0x9a, 0x3d, 0x67, 0xee, 0xbc, 0xf9, 0xb, 0x16, 0x2e, 0x5a, 0xbc, 0x64, 0xe9, 0xb2, 0xe5, 0x2b, 0x66, 0x31, 0xac, 0x44, 0x3, 0x0, 0xa4, 0xd7, 0x4d, 0x73, 0x12, 0x21, 0x19, 0xde, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x1, 0xfe, 0x50, 0x4c, 0x54, 0x45, 0x29, 0x3a, 0x40, 0x2d, 0x3e, 0x44, 0x26, 0x34, 0x3b, 0x24, 0x34, 0x39, 0x23, 0x31, 0x38, 0x22, 0x31, 0x37, 0x22, 0x31, 0x37, 0x22, 0x30, 0x36, 0x22, 0x31, 0x36, 0x26, 0x34, 0x3c, 0x32, 0x44, 0x4c, 0x26, 0x34, 0x39, 0x23, 0x31, 0x36, 0x21, 0x2e, 0x34, 0x1f, 0x2c, 0x30, 0x1f, 0x2b, 0x2f, 0x1f, 0x2a, 0x2e, 0x1e, 0x2b, 0x2f, 0x1f, 0x2b, 0x2e, 0x36, 0x4b, 0x52, 0x25, 0x33, 0x38, 0x20, 0x2f, 0x32, 0x1c, 0x29, 0x2e, 0x1b, 0x26, 0x2a, 0x1a, 0x23, 0x26, 0x18, 0x22, 0x26, 0x19, 0x22, 0x26, 0x19, 0x23, 0x26, 0x19, 0x26, 0x29, 0x20, 0x2d, 0x32, 0x25, 0x31, 0x38, 0x3c, 0x51, 0x59, 0x23, 0x31, 0x37, 0x1f, 0x2b, 0x31, 0x1a, 0x25, 0x2b, 0x17, 0x20, 0x24, 0x15, 0x1c, 0x21, 0x14, 0x1b, 0x21, 0x14, 0x1c, 0x21, 0x13, 0x1b, 0x21, 0x15, 0x1d, 0x21, 0x1a, 0x25, 0x2a, 0x40, 0x57, 0x60, 0x23, 0x31, 0x36, 0x1f, 0x2b, 0x31, 0x1b, 0x25, 0x29, 0x16, 0x1e, 0x23, 0x14, 0x1b, 0x1d, 0x12, 0x19, 0x1d, 0x12, 0x1b, 0x1d, 0x14, 0x1a, 0x1d, 0x45, 0x5e, 0x67, 0x22, 0x32, 0x37, 0x20, 0x2d, 0x31, 0x1a, 0x26, 0x2a, 0x15, 0x1f, 0x25, 0x14, 0x1c, 0x1f, 0x12, 0x1b, 0x1f, 0x12, 0x1b, 0x20, 0x14, 0x1b, 0x1f, 0x15, 0x1e, 0x24, 0x1a, 0x25, 0x29, 0x4b, 0x64, 0x6d, 0x23, 0x32, 0x38, 0x20, 0x2e, 0x32, 0x1b, 0x27, 0x2b, 0x17, 0x22, 0x27, 0x16, 0x1e, 0x23, 0x14, 0x1e, 0x23, 0x16, 0x20, 0x24, 0x14, 0x1e, 0x22, 0x15, 0x1e, 0x22, 0x17, 0x21, 0x27, 0x1c, 0x27, 0x2c, 0x4f, 0x6a, 0x75, 0x21, 0x2f, 0x33, 0x1d, 0x29, 0x2d, 0x19, 0x23, 0x2a, 0x18, 0x22, 0x27, 0x16, 0x21, 0x27, 0x18, 0x23, 0x29, 0x17, 0x21, 0x26, 0x19, 0x23, 0x29, 0x1c, 0x28, 0x2d, 0x21, 0x2e, 0x33, 0x54, 0x70, 0x7c, 0x23, 0x33, 0x38, 0x22, 0x30, 0x34, 0x1e, 0x2a, 0x2f, 0x1a, 0x26, 0x2d, 0x1a, 0x25, 0x2b, 0x19, 0x25, 0x2b, 0x1a, 0x26, 0x2d, 0x1a, 0x26, 0x2c, 0x18, 0x25, 0x2a, 0x1a, 0x24, 0x2a, 0x1a, 0x25, 0x2c, 0x1d, 0x2a, 0x2f, 0x22, 0x2f, 0x34, 0x59, 0x77, 0x82, 0x23, 0x33, 0x39, 0x22, 0x30, 0x35, 0x1f, 0x2c, 0x31, 0x1c, 0x28, 0x30, 0x1c, 0x28, 0x2e, 0x1b, 0x29, 0x2f, 0x1c, 0x2a, 0x31, 0x1b, 0x28, 0x2f, 0x1c, 0x28, 0x2d, 0x1b, 0x27, 0x2f, 0x1f, 0x2b, 0x31, 0x5e, 0x7d, 0x8a, 0x24, 0x34, 0x39, 0x21, 0x2f, 0x37, 0x20, 0x2d, 0x34, 0x1d, 0x2b, 0x33, 0x1d, 0x2b, 0x32, 0x1d, 0x2d, 0x35, 0x1e, 0x2e, 0x36, 0x1f, 0x2e, 0x36, 0x1d, 0x2b, 0x34, 0x1d, 0x2b, 0x31, 0x1d, 0x2b, 0x32, 0x20, 0x2d, 0x32, 0x21, 0x2f, 0x36, 0x63, 0x83, 0x90, 0x25, 0x34, 0x39, 0x21, 0x31, 0x36, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x34, 0x1f, 0x2e, 0x36, 0x20, 0x31, 0x39, 0x21, 0x33, 0x3b, 0x21, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1e, 0x2d, 0x33, 0x1f, 0x2d, 0x33, 0x21, 0x30, 0x36, 0x67, 0x8a, 0x97, 0x24, 0x33, 0x39, 0x20, 0x30, 0x36, 0x1f, 0x2f, 0x35, 0x21, 0x30, 0x37, 0x22, 0x32, 0x39, 0x21, 0x35, 0x3e, 0x24, 0x37, 0x41, 0x24, 0x36, 0x41, 0x21, 0x33, 0x3c, 0x21, 0x31, 0x38, 0x1e, 0x2f, 0x35, 0x1e, 0x2e, 0x35, 0x20, 0x2e, 0x35, 0x24, 0x31, 0x39, 0x6c, 0x90, 0x9e, 0x22, 0x30, 0x36, 0x1f, 0x2e, 0x36, 0x20, 0x30, 0x36, 0x20, 0x31, 0x39, 0x23, 0x34, 0x3d, 0x23, 0x37, 0x41, 0x26, 0x3c, 0x47, 0x26, 0x3b, 0x46, 0x22, 0x35, 0x3f, 0x22, 0x32, 0x3b, 0x1f, 0x30, 0x37, 0x1f, 0x2e, 0x35, 0x1f, 0x2d, 0x35, 0x21, 0x30, 0x36, 0x72, 0x96, 0xa5, 0x7e, 0x8c, 0xc3, 0xb0, 0x0, 0x0, 0x0, 0xaa, 0x74, 0x52, 0x4e, 0x53, 0xc3, 0xc3, 0xe6, 0xd7, 0xcb, 0xc3, 0xbf, 0xbe, 0xbd, 0xe5, 0xc3, 0xd7, 0xc0, 0xac, 0xa0, 0x9a, 0x98, 0x98, 0x98, 0xc3, 0xcb, 0xac, 0x92, 0x82, 0x7b, 0x78, 0x78, 0x7b, 0x82, 0xac, 0xcb, 0xc3, 0xc3, 0xa0, 0x82, 0x6f, 0x67, 0x64, 0x63, 0x64, 0x67, 0x82, 0xc3, 0xbf, 0x9a, 0x7a, 0x67, 0x5e, 0x5b, 0x5a, 0x5e, 0xc3, 0xbd, 0x98, 0x78, 0x64, 0x5b, 0x57, 0x57, 0x5b, 0x64, 0x78, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x78, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x77, 0x98, 0xc3, 0xbd, 0x98, 0x77, 0x63, 0x5a, 0x57, 0x56, 0x57, 0x5a, 0x63, 0x77, 0xc3, 0xbb, 0x96, 0x76, 0x63, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x5a, 0x63, 0x76, 0x96, 0xc3, 0xb5, 0x92, 0x75, 0x62, 0x5a, 0x57, 0x56, 0x56, 0x57, 0x59, 0x62, 0x74, 0x92, 0xc3, 0xa9, 0x8b, 0x71, 0x61, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x61, 0x71, 0x8b, 0xa9, 0xc3, 0x95, 0x7e, 0x6b, 0x5e, 0x59, 0x57, 0x56, 0x56, 0x57, 0x59, 0x5e, 0x6b, 0x7e, 0x95, 0xc3, 0x4f, 0x78, 0x99, 0x30, 0x0, 0x0, 0x0, 0x67, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5c, 0x8b, 0x5, 0x2, 0xc3, 0x30, 0xc, 0xc4, 0x72, 0x5d, 0x39, 0xf4, 0xff, 0x67, 0x8e, 0x79, 0x1a, 0x37, 0xa0, 0xa0, 0x6d, 0x5d, 0x6b, 0x2a, 0x5a, 0xfd, 0x30, 0xe6, 0x1, 0xf4, 0xa7, 0x76, 0xfa, 0x37, 0x74, 0x7, 0x98, 0x52, 0x83, 0x46, 0x97, 0x41, 0xfb, 0xd6, 0x2d, 0x86, 0xae, 0xfe, 0x84, 0x6b, 0x6d, 0x32, 0x6e, 0x58, 0x28, 0x22, 0x3a, 0x41, 0x32, 0xde, 0xd7, 0x6a, 0x67, 0x5b, 0xb7, 0xb7, 0xc9, 0xb0, 0xd8, 0xd6, 0x3a, 0x65, 0x34, 0xb4, 0x21, 0x8f, 0x1c, 0x3, 0x6d, 0x21, 0x84, 0x5d, 0x32, 0x8a, 0x48, 0x22, 0x6e, 0xda, 0xa8, 0x92, 0x36, 0x3e, 0x87, 0xea, 0x7b, 0x7e, 0x0, 0x62, 0xa8, 0x25, 0xad, 0x68, 0x1d, 0x7d, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tooltip_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xdd, 0xdd, 0xdd, 0xfe, 0x3f, 0x83, 0xa9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xc, 0x81, 0xb3, 0x51, 0x63, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x44, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0xc5, 0xcf, 0xbb, 0x15, 0x0, 0x20, 0x8, 0x43, 0xd1, 0x80, 0x28, 0xf8, 0x41, 0xf7, 0xdf, 0xd6, 0x86, 0x63, 0xa1, 0x3, 0x78, 0xcb, 0x54, 0x79, 0x0, 0x71, 0x92, 0x90, 0x98, 0x0, 0xca, 0x45, 0x2d, 0x68, 0xc9, 0x4, 0xae, 0xad, 0x7b, 0xe8, 0xad, 0x32, 0x44, 0xe7, 0x1a, 0x61, 0x4d, 0x15, 0x88, 0xf9, 0x38, 0xdc, 0xfe, 0xd, 0xf7, 0xb1, 0xe7, 0xfa, 0x13, 0x77, 0xe7, 0x6f, 0xdc, 0x6c, 0x9, 0x25, 0x82, 0x67, 0x68, 0x78, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x30, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x2c, 0x2f, 0x48, 0x46, 0x4a, 0xdd, 0xdd, 0xdd, 0x4c, 0x4a, 0x4e, 0x48, 0x46, 0x4a, 0x40, 0x3e, 0x42, 0xbc, 0x3, 0x4f, 0xe9, 0x0, 0x0, 0x0, 0xd, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0xf5, 0xfe, 0xcc, 0xff, 0xb7, 0x4a, 0xbe, 0x0, 0x0, 0x0, 0x38, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x54, 0x76, 0x1, 0x2, 0x23, 0x1, 0x6, 0xd1, 0xf4, 0xe, 0x20, 0x28, 0xb, 0x64, 0xd0, 0x5c, 0x7d, 0x17, 0x8, 0x76, 0x4d, 0x62, 0x70, 0x7f, 0x7f, 0x6, 0x8, 0xfe, 0x95, 0x30, 0x78, 0xdc, 0x1, 0x31, 0xce, 0xb6, 0x50, 0xc8, 0x80, 0x1b, 0x8, 0xb7, 0x2, 0x6e, 0x29, 0xdc, 0x19, 0x0, 0xcf, 0x24, 0x4d, 0xb3, 0xd0, 0x4d, 0xb9, 0x40, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xd, 0xf6, 0xb4, 0x61, 0xf5, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_bg_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x95, 0xce, 0x31, 0x12, 0x2, 0x21, 0x10, 0x44, 0xd1, 0x3f, 0x40, 0xa1, 0x44, 0xa6, 0x46, 0xde, 0x63, 0x4f, 0xe5, 0x15, 0x38, 0xb2, 0xd6, 0x6, 0xb0, 0xc8, 0x30, 0x6, 0x96, 0xac, 0x56, 0x99, 0xf8, 0xb3, 0x7e, 0x51, 0xcb, 0xf9, 0x1a, 0xb3, 0x3f, 0xa, 0xaf, 0xc, 0xad, 0x2d, 0xcb, 0xe5, 0x76, 0x38, 0x5, 0x76, 0xec, 0x6c, 0xf7, 0xe0, 0x53, 0xe0, 0x13, 0xa1, 0x27, 0x27, 0x43, 0x26, 0x81, 0x20, 0xc8, 0x70, 0xfc, 0xe8, 0xf, 0x34, 0x67, 0xd8, 0x9c, 0x86, 0x61, 0x2e, 0x68, 0xe9, 0x91, 0xaf, 0x4b, 0x5a, 0x7d, 0x2a, 0x2c, 0x3, 0xed, 0xef, 0x1e, 0x6b, 0xcb, 0x4f, 0xa6, 0x66, 0x2b, 0x25, 0x6, 0x1, 0x37, 0x40, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x94, 0xc8, 0x67, 0x6b, 0x60, 0xe6, 0x60, 0x64, 0x80, 0x80, 0xff, 0xc, 0x7f, 0x7f, 0xfc, 0x6a, 0x60, 0x94, 0xfb, 0xc0, 0xce, 0xcf, 0xc2, 0x80, 0x10, 0xfc, 0xc3, 0xf0, 0xf3, 0x23, 0xa3, 0xe2, 0x4f, 0xe, 0x36, 0x54, 0xc1, 0x1f, 0xbf, 0x18, 0x95, 0xbe, 0x73, 0x70, 0xb0, 0x30, 0xc0, 0x1, 0x48, 0xf0, 0x7, 0x85, 0x82, 0x58, 0x2d, 0xc2, 0xe6, 0xa4, 0x4f, 0x20, 0xc7, 0x37, 0x32, 0xb3, 0x23, 0x39, 0xfe, 0xfb, 0xaf, 0x46, 0x0, 0xee, 0x2a, 0x2f, 0xce, 0x4c, 0x47, 0x66, 0xf6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_bg_focus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x6, 0x0, 0x0, 0x0, 0x73, 0x7a, 0x7a, 0xf4, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xdb, 0xb, 0x4, 0x12, 0x2d, 0x3a, 0xb5, 0x1b, 0x14, 0x49, 0x0, 0x0, 0x2, 0xd9, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xc5, 0x57, 0x31, 0x8e, 0x14, 0x31, 0x10, 0xac, 0xea, 0x3d, 0x11, 0x91, 0xdf, 0x49, 0xf7, 0xf, 0x78, 0x1, 0xf, 0x40, 0x84, 0x44, 0x10, 0xf2, 0x20, 0x42, 0x62, 0x12, 0xc4, 0x3, 0x78, 0x1, 0xff, 0x21, 0x42, 0xb8, 0x8a, 0xc0, 0x1e, 0x4f, 0xdb, 0xe3, 0xb9, 0xbb, 0x45, 0x20, 0x7c, 0x1a, 0xdd, 0x8c, 0xec, 0xed, 0xae, 0xae, 0xea, 0xee, 0xe9, 0xe1, 0xfd, 0xed, 0xdd, 0xb, 0x0, 0xdf, 0xf1, 0x7f, 0xd6, 0x4b, 0xde, 0xdf, 0xde, 0xf9, 0xcb, 0xeb, 0x9f, 0xff, 0xc5, 0xfb, 0x9b, 0xaf, 0xcf, 0x70, 0xb3, 0x3d, 0x3c, 0xff, 0xf0, 0xd, 0x24, 0x11, 0x71, 0x1, 0x49, 0x90, 0x4, 0x0, 0x4, 0xd3, 0x2f, 0xb6, 0x7b, 0xf, 0xff, 0x86, 0x2d, 0x8c, 0x47, 0x60, 0x1b, 0x0, 0x61, 0x1b, 0xb6, 0x21, 0x15, 0xd8, 0xc6, 0x8f, 0x8f, 0xaf, 0x0, 0x60, 0x7, 0x0, 0xa2, 0x2, 0x20, 0x11, 0x41, 0x30, 0xa2, 0x2, 0xc1, 0xb5, 0x0, 0x8c, 0x79, 0xd9, 0x80, 0x24, 0x48, 0x0, 0x48, 0x28, 0x9d, 0xb9, 0xd9, 0xd, 0xb0, 0x31, 0x40, 0x5c, 0x2e, 0x1, 0x46, 0x0, 0xd, 0xd0, 0xb6, 0xef, 0x35, 0x8e, 0x13, 0x6, 0xbc, 0x33, 0x60, 0xa3, 0x9a, 0x11, 0x6c, 0x82, 0xe6, 0xa, 0x0, 0x86, 0xe8, 0x2f, 0x71, 0xa9, 0xf7, 0x9c, 0x4d, 0x1f, 0x69, 0x5e, 0x1, 0xd8, 0xa3, 0x37, 0xa4, 0xa, 0x27, 0x1a, 0x13, 0x79, 0x25, 0x9, 0xaa, 0xe3, 0xd8, 0x9c, 0x5f, 0xea, 0x7d, 0x67, 0x80, 0xd7, 0x31, 0x20, 0xab, 0x3, 0x20, 0x5, 0x20, 0x2a, 0x13, 0x41, 0x40, 0x2b, 0x0, 0x76, 0x22, 0xb9, 0xb1, 0xd1, 0x92, 0x91, 0x5d, 0x86, 0xf3, 0x35, 0xef, 0x5, 0xa3, 0x25, 0x60, 0xd, 0x6e, 0x66, 0xe5, 0x0, 0xc0, 0xa8, 0x54, 0x1, 0x6, 0xe9, 0x16, 0x9f, 0x4f, 0xdc, 0x1c, 0x13, 0xed, 0xb1, 0x55, 0x31, 0x78, 0x70, 0x8e, 0xca, 0xcb, 0x8e, 0x6a, 0xbe, 0x56, 0x88, 0x67, 0x38, 0xdb, 0x75, 0x58, 0x76, 0xdf, 0x1b, 0x6c, 0x7a, 0xc, 0x6c, 0x90, 0x0, 0xfd, 0xe0, 0xe8, 0x98, 0xe4, 0x4, 0xe2, 0x69, 0xc, 0xe4, 0x40, 0xaa, 0xf9, 0x63, 0x70, 0xbb, 0x4, 0x16, 0x9c, 0x12, 0xa7, 0x3e, 0xe7, 0x38, 0xb3, 0x4, 0x2b, 0x6f, 0xc7, 0x23, 0xbb, 0xc3, 0x66, 0xdb, 0x38, 0x7, 0x0, 0xcf, 0x32, 0x34, 0xb6, 0x52, 0xb4, 0x3c, 0xf7, 0x35, 0x10, 0xd9, 0xf7, 0x6, 0x56, 0x93, 0x4, 0x4b, 0x0, 0x43, 0x28, 0xf9, 0x4a, 0x3d, 0x6e, 0xb0, 0x3e, 0x9, 0x31, 0xa9, 0x62, 0xd6, 0xc4, 0x36, 0x9c, 0xf2, 0xd9, 0xbd, 0x41, 0x2d, 0x24, 0x68, 0xc7, 0x1f, 0x62, 0x60, 0x6a, 0xc1, 0x7f, 0x95, 0x81, 0xaa, 0x13, 0x61, 0x9, 0xa6, 0xf6, 0x1c, 0x30, 0x17, 0x11, 0x6f, 0x48, 0x12, 0x3b, 0xf3, 0x7b, 0xc2, 0xeb, 0xca, 0x92, 0xb7, 0x72, 0x5f, 0x31, 0x90, 0x11, 0x4e, 0x48, 0xb3, 0x13, 0xa6, 0xcc, 0x3e, 0x51, 0x60, 0x91, 0x53, 0x55, 0x87, 0xf3, 0x2a, 0x98, 0xff, 0x7c, 0x6c, 0x1a, 0xf9, 0xec, 0xda, 0xeb, 0x94, 0x13, 0x43, 0xdd, 0x3f, 0x2a, 0x41, 0xd2, 0xb, 0x27, 0x65, 0xe8, 0x13, 0x20, 0x29, 0x3f, 0xc6, 0x7c, 0x48, 0x65, 0x8, 0x41, 0xda, 0xa4, 0xd5, 0x11, 0xc0, 0x2a, 0x61, 0xce, 0x18, 0x58, 0xd2, 0xbe, 0x98, 0x48, 0x96, 0xdd, 0xf5, 0xbc, 0x11, 0x65, 0xb6, 0x12, 0x55, 0xcb, 0xde, 0xb3, 0x78, 0x27, 0x78, 0xdc, 0xcb, 0x72, 0xe6, 0xd7, 0x8a, 0x27, 0xe6, 0x52, 0x1f, 0x10, 0xe0, 0xb8, 0x92, 0x81, 0xb4, 0x3f, 0x1, 0x1d, 0xed, 0x6c, 0xd4, 0xef, 0x12, 0x2f, 0x73, 0xa0, 0xe, 0xf, 0x42, 0x24, 0x20, 0xf, 0x91, 0xbf, 0x6e, 0x44, 0xfb, 0xde, 0xa1, 0x4, 0xa5, 0x7, 0xaa, 0xa0, 0x23, 0xad, 0xd4, 0x4b, 0xaa, 0x73, 0x0, 0xb7, 0xa1, 0x82, 0x87, 0x46, 0x74, 0x9a, 0xf, 0x48, 0x5d, 0xb3, 0xd, 0xa2, 0xb0, 0x96, 0xe5, 0x7d, 0xc8, 0x81, 0x19, 0xf1, 0x36, 0x2b, 0xba, 0xbd, 0x5e, 0xb3, 0xb7, 0x55, 0x12, 0x76, 0x90, 0xc4, 0x3a, 0x1, 0xa7, 0x66, 0xbc, 0x3, 0x90, 0xe0, 0x8, 0xc8, 0x42, 0x29, 0xa5, 0x7b, 0xa2, 0x63, 0x9f, 0x88, 0x26, 0xa, 0x9c, 0x85, 0x6f, 0x7b, 0x2b, 0x0, 0x92, 0x51, 0x4a, 0x81, 0x5c, 0x2a, 0xcb, 0x2a, 0x47, 0x0, 0xb0, 0x2b, 0x8, 0x11, 0x62, 0x20, 0x58, 0x20, 0x47, 0x77, 0x5a, 0xe5, 0x98, 0x43, 0x3f, 0x2, 0xd8, 0xc1, 0xa5, 0x39, 0x40, 0x82, 0xb5, 0xd9, 0xd7, 0x5a, 0x2, 0x59, 0x80, 0x9, 0x8a, 0x0, 0x7f, 0x1, 0x8, 0x44, 0x8, 0x64, 0xe0, 0xe8, 0x6e, 0x4b, 0xb4, 0x87, 0xa6, 0xc4, 0x3d, 0x17, 0x24, 0xa3, 0xa8, 0xc0, 0x32, 0x8a, 0xea, 0x33, 0x67, 0x0, 0x6e, 0xc, 0x94, 0x5e, 0xe2, 0x46, 0x11, 0x3a, 0x0, 0x82, 0x57, 0x1, 0x98, 0xbf, 0xb, 0xa4, 0xf6, 0x55, 0xd4, 0x2a, 0xe1, 0x0, 0x40, 0xf5, 0xf3, 0xa5, 0xd3, 0x16, 0x6a, 0xb4, 0xa7, 0x19, 0xfa, 0x4f, 0x18, 0xe8, 0x2c, 0x34, 0xfb, 0x92, 0x20, 0xbb, 0xf, 0xa3, 0x1d, 0xc0, 0xcd, 0xe7, 0xb7, 0x83, 0xf9, 0xf2, 0x24, 0xd3, 0xd7, 0xaf, 0x40, 0x9a, 0x84, 0x1, 0xf0, 0xfe, 0xf6, 0xee, 0x1d, 0x80, 0x4f, 0xff, 0xc8, 0xdf, 0x63, 0xeb, 0xfd, 0x6f, 0x3, 0x74, 0x35, 0xa7, 0x2a, 0xf0, 0x17, 0xed, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x6, 0x0, 0x0, 0x0, 0x73, 0x7a, 0x7a, 0xf4, 0x0, 0x0, 0x2, 0x7f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x91, 0x16, 0x97, 0x30, 0x63, 0x60, 0x60, 0x38, 0xc9, 0x30, 0x30, 0xc0, 0x9c, 0x11, 0xe8, 0x80, 0xff, 0x6b, 0x3, 0x7f, 0xd, 0x88, 0xed, 0xc1, 0xeb, 0xd9, 0x18, 0x58, 0x60, 0x1c, 0x9e, 0xac, 0xdd, 0x0, 0x5a, 0xcb, 0x6, 0xc5, 0x99, 0x23, 0x6, 0xa2, 0xaf, 0xe4, 0xb3, 0xe5, 0x8a, 0x39, 0x69, 0xf8, 0xcf, 0xa8, 0x82, 0x2d, 0x52, 0xe0, 0x1e, 0x61, 0x63, 0xe0, 0x93, 0x29, 0x7a, 0x66, 0xdb, 0xdb, 0x7a, 0x5d, 0x92, 0x96, 0x45, 0x12, 0x55, 0xf, 0x24, 0xbd, 0x4, 0x50, 0x22, 0x81, 0x98, 0x70, 0x96, 0x63, 0x2b, 0x91, 0x3d, 0xdb, 0x80, 0xb0, 0xfd, 0x52, 0xf7, 0x85, 0x6d, 0xfe, 0xf9, 0xfb, 0x2f, 0x0, 0x2, 0x80, 0x18, 0x80, 0xa7, 0x4a, 0xa8, 0x6a, 0x40, 0xf8, 0x15, 0xc0, 0x9c, 0x61, 0x43, 0x77, 0xd3, 0xd, 0x48, 0x34, 0xce, 0x5e, 0x0, 0x84, 0xd0, 0x24, 0xe7, 0xf1, 0xa8, 0x17, 0x0, 0x3, 0x94, 0x7d, 0xef, 0x1c, 0xec, 0xe, 0x98, 0x38, 0x60, 0x33, 0xc7, 0x34, 0xb6, 0x90, 0xb5, 0x1, 0xf0, 0x76, 0xfb, 0x47, 0x3d, 0xe6, 0x59, 0x62, 0x89, 0x5, 0x60, 0x8f, 0xb1, 0x7d, 0x70, 0x6a, 0x9c, 0x20, 0xf1, 0x5e, 0x82, 0x49, 0x5c, 0xff, 0x27, 0x7f, 0xcc, 0x73, 0x1c, 0xd0, 0x6f, 0xe, 0xb4, 0x3b, 0x0, 0x52, 0x3, 0x35, 0x4e, 0x94, 0xa0, 0x37, 0x0, 0x9b, 0xa4, 0x18, 0x37, 0x5e, 0xd2, 0xe8, 0xd3, 0x2d, 0xd7, 0xbd, 0x52, 0x61, 0x3b, 0x97, 0x3b, 0x5c, 0xb9, 0x3, 0x98, 0xb1, 0xa, 0x8c, 0x34, 0xeb, 0x68, 0xef, 0xc2, 0x9f, 0x22, 0xc, 0x4e, 0xf2, 0x80, 0x42, 0xa8, 0x4e, 0xdd, 0x89, 0x8f, 0x50, 0xb4, 0x84, 0x9d, 0xbd, 0xb7, 0x33, 0x6d, 0xc0, 0x7b, 0x9, 0xc8, 0x17, 0xdf, 0x13, 0x4b, 0xca, 0xf3, 0x2f, 0xe, 0x24, 0x69, 0x8e, 0xf7, 0x68, 0x2d, 0x81, 0xfb, 0xa9, 0xfc, 0xe2, 0xbc, 0x73, 0xdc, 0x51, 0x7c, 0x1d, 0x9, 0xdd, 0x1, 0x72, 0xb6, 0x59, 0x1, 0x26, 0xe, 0xc2, 0xb8, 0x85, 0xf7, 0xe, 0xd8, 0x71, 0xe2, 0x9e, 0x6e, 0xae, 0x8e, 0x18, 0x2d, 0x0, 0x9, 0x1f, 0xd2, 0xbd, 0x17, 0xb4, 0x14, 0xc2, 0xc7, 0x29, 0x2, 0x33, 0x9f, 0x1c, 0xc5, 0xbc, 0x3, 0x5b, 0x9, 0x8c, 0xf9, 0xe2, 0x80, 0xff, 0xa0, 0x3, 0x53, 0x27, 0xe1, 0x6e, 0xac, 0x26, 0x3d, 0x60, 0x2d, 0x37, 0x36, 0xb9, 0x26, 0xc7, 0xa3, 0xd9, 0x9a, 0x2e, 0xea, 0xa7, 0x7a, 0x3, 0x38, 0x8, 0x23, 0xb8, 0x25, 0x51, 0x3a, 0xfb, 0xc3, 0x5c, 0x2c, 0x0, 0xf0, 0x69, 0xa, 0xce, 0x8f, 0x47, 0x5b, 0x18, 0x7f, 0x9f, 0x46, 0x3, 0x6f, 0x73, 0xff, 0xb5, 0x4, 0x26, 0xf5, 0x62, 0x1f, 0x43, 0xbc, 0x81, 0xa4, 0x3f, 0x12, 0xeb, 0x18, 0xd2, 0x74, 0x37, 0xf6, 0xe8, 0xcb, 0x1f, 0xa2, 0x88, 0x3d, 0x16, 0x1e, 0xed, 0x25, 0x38, 0xc5, 0x5e, 0x82, 0xd3, 0x2d, 0x13, 0x89, 0x25, 0x74, 0x47, 0xf0, 0xfb, 0x9e, 0xf1, 0x32, 0x86, 0x29, 0xc6, 0xe2, 0x0, 0xd, 0xae, 0x1f, 0x1d, 0x30, 0x9, 0x2d, 0xf6, 0x47, 0xcd, 0xc8, 0x23, 0x3e, 0x4c, 0x41, 0x77, 0x53, 0xae, 0x2f, 0x0, 0xbe, 0x97, 0xc0, 0xb7, 0xbd, 0xfb, 0x8, 0x76, 0x20, 0x56, 0x80, 0x50, 0x32, 0x5f, 0x46, 0x12, 0x68, 0xf6, 0x24, 0xa1, 0xb5, 0xf9, 0xd7, 0x9f, 0x25, 0xc9, 0x9c, 0x75, 0x81, 0x1b, 0x3e, 0x4f, 0xc1, 0xe8, 0x24, 0x16, 0x42, 0xd2, 0x40, 0x1c, 0xd9, 0xb6, 0x26, 0xc, 0xa4, 0x58, 0x1b, 0x70, 0x3e, 0x2c, 0x0, 0xdd, 0xb8, 0x8a, 0x76, 0x73, 0x5d, 0x57, 0x32, 0xc9, 0x45, 0xfe, 0x23, 0x3a, 0x2c, 0x8, 0x52, 0x32, 0xef, 0x0, 0xdd, 0x7e, 0x9d, 0xd9, 0xbe, 0xc6, 0xe5, 0xbe, 0xd6, 0x31, 0x1c, 0x88, 0x16, 0xad, 0xa2, 0x74, 0xd1, 0xae, 0x24, 0x95, 0xc4, 0xad, 0x6, 0xb, 0x0, 0x59, 0x1c, 0x67, 0xe7, 0x5c, 0x33, 0x6b, 0xef, 0x25, 0x68, 0x37, 0x58, 0xa8, 0x5, 0xfa, 0x17, 0x28, 0xaa, 0x1a, 0xa9, 0x96, 0x46, 0x77, 0x1c, 0xf8, 0x16, 0x71, 0xa0, 0x2f, 0xfc, 0x5a, 0xe7, 0x5d, 0x6b, 0x13, 0x76, 0x73, 0x89, 0x38, 0x72, 0x35, 0x1, 0x10, 0xfa, 0x9, 0x20, 0xe, 0xa4, 0xa9, 0x9f, 0xba, 0xc6, 0x5, 0x2f, 0x0, 0x6d, 0x43, 0x77, 0x6c, 0xab, 0x66, 0x6c, 0xa7, 0x7f, 0x73, 0x60, 0x77, 0x21, 0xe7, 0xf7, 0x53, 0x36, 0x75, 0x2, 0xfc, 0x37, 0x96, 0x15, 0xd1, 0x28, 0xc6, 0xff, 0x65, 0xa0, 0xd, 0x60, 0x2, 0x63, 0x4, 0x0, 0xf5, 0x8e, 0x13, 0x81, 0xf4, 0x3c, 0x86, 0x81, 0x1, 0x49, 0x0, 0x33, 0xd4, 0x35, 0xaa, 0x8d, 0x7e, 0xfe, 0xa5, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_cursor_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0xe, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x96, 0xdd, 0xe3, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xc6, 0x50, 0x4c, 0x54, 0x45, 0xd4, 0xab, 0x9e, 0xd3, 0xaa, 0x9d, 0xd4, 0xab, 0x9e, 0xd3, 0xaa, 0x9d, 0xd4, 0xab, 0x9c, 0xd4, 0xac, 0x9e, 0xd5, 0xaf, 0xa3, 0xd5, 0xb0, 0xa3, 0xd5, 0xaf, 0xa3, 0xd5, 0xad, 0xa1, 0xd5, 0xaf, 0xa3, 0xd5, 0xad, 0xa1, 0xd5, 0xb0, 0xa3, 0xd6, 0xad, 0xa0, 0xd6, 0xad, 0xa0, 0xd5, 0xb0, 0xa3, 0xd7, 0xb1, 0xa5, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa5, 0xd7, 0xb1, 0xa5, 0xd8, 0xb3, 0xa8, 0xd8, 0xb5, 0xaa, 0xda, 0xb3, 0xa8, 0xda, 0xb3, 0xa8, 0xd8, 0xb3, 0xa8, 0xd8, 0xb5, 0xaa, 0xda, 0xb3, 0xa8, 0xd8, 0xb3, 0xa8, 0xdb, 0xb7, 0xad, 0xda, 0xb8, 0xae, 0xdb, 0xb9, 0xad, 0xdb, 0xb9, 0xad, 0xdb, 0xb7, 0xad, 0xdd, 0xba, 0xb1, 0xdd, 0xbb, 0xb1, 0xdd, 0xbd, 0xb1, 0xdd, 0xbf, 0xb3, 0xdd, 0xbd, 0xb3, 0xdf, 0xc1, 0xb7, 0xdf, 0xc0, 0xb5, 0xdf, 0xbf, 0xb5, 0xe1, 0xc3, 0xb9, 0xe1, 0xc3, 0xbb, 0xe1, 0xc5, 0xbb, 0xe2, 0xc7, 0xbe, 0xe1, 0xc5, 0xbd, 0xe4, 0xcb, 0xc2, 0xe3, 0xca, 0xc1, 0xe3, 0xcb, 0xc3, 0xe6, 0xce, 0xc6, 0xe6, 0xd0, 0xc6, 0xe8, 0xd1, 0xca, 0xe8, 0xd1, 0xca, 0xe8, 0xd0, 0xca, 0xe8, 0xcf, 0xca, 0xb7, 0x7d, 0x69, 0xb1, 0x77, 0x63, 0xac, 0x73, 0x5c, 0xa6, 0x69, 0x56, 0x9c, 0x67, 0x54, 0x93, 0x62, 0x51, 0x88, 0x60, 0x50, 0x0, 0x0, 0x0, 0x39, 0x76, 0x1d, 0xc2, 0x0, 0x0, 0x0, 0x3a, 0x74, 0x52, 0x4e, 0x53, 0x32, 0x2f, 0x30, 0x33, 0x38, 0x40, 0x32, 0x2f, 0x2f, 0x2f, 0x31, 0x33, 0x33, 0x33, 0x36, 0x38, 0x31, 0x2f, 0x30, 0x31, 0x33, 0x33, 0x35, 0x2f, 0x2f, 0x2f, 0x30, 0x32, 0x33, 0x33, 0x33, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x32, 0x2f, 0x2f, 0x2f, 0xb8, 0xf, 0x95, 0x41, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x41, 0x89, 0xde, 0x6c, 0x4e, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x9e, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcf, 0x5b, 0x17, 0x81, 0x40, 0x14, 0x5, 0xe0, 0xe3, 0x9a, 0x4b, 0x2e, 0x31, 0x14, 0x62, 0x22, 0x53, 0x4d, 0x83, 0x44, 0xc, 0xa6, 0xa9, 0xf9, 0xff, 0xbf, 0xca, 0xd4, 0x5a, 0x1e, 0x7c, 0xfb, 0xec, 0xb5, 0x9f, 0xf, 0x7c, 0xfe, 0x80, 0x80, 0x86, 0xd6, 0x6c, 0x69, 0xed, 0x8e, 0x80, 0xbc, 0x6b, 0x18, 0xbd, 0xfe, 0x60, 0x68, 0x9a, 0xa3, 0xf1, 0x24, 0x7, 0x39, 0xb5, 0x2c, 0x6b, 0x36, 0x47, 0x68, 0x81, 0xd0, 0x52, 0x42, 0x61, 0x3b, 0xb6, 0xb3, 0x72, 0xd6, 0x1b, 0x77, 0xeb, 0xee, 0xa, 0x28, 0x31, 0xc6, 0x9e, 0x87, 0x3d, 0xbc, 0x3f, 0xf8, 0x7e, 0x9, 0xea, 0x48, 0x8, 0x9, 0xc2, 0x28, 0x22, 0x21, 0x9, 0x14, 0x28, 0x1a, 0x33, 0x16, 0xd3, 0x98, 0xb2, 0x98, 0x51, 0x5, 0xe5, 0x49, 0x3b, 0xeb, 0x5e, 0x74, 0x4a, 0x28, 0x92, 0xca, 0xb5, 0xbe, 0xa4, 0x0, 0x99, 0xde, 0xd2, 0xca, 0xbd, 0x5a, 0x9, 0x79, 0x96, 0x3d, 0xb2, 0x9f, 0x1c, 0xc4, 0x93, 0xf3, 0x17, 0xaf, 0xfb, 0xe6, 0x2, 0xfe, 0x5f, 0xf8, 0x2, 0x30, 0xbc, 0x1f, 0xb4, 0x2b, 0xfc, 0x80, 0xca, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0xe, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x96, 0xdd, 0xe3, 0x0, 0x0, 0x0, 0xc3, 0x50, 0x4c, 0x54, 0x45, 0xd4, 0xab, 0x9e, 0xd3, 0xaa, 0x9d, 0xd4, 0xab, 0x9e, 0xd3, 0xaa, 0x9d, 0xd4, 0xab, 0x9c, 0xd4, 0xac, 0x9e, 0xd5, 0xaf, 0xa3, 0xd5, 0xb0, 0xa3, 0xd5, 0xaf, 0xa3, 0xd5, 0xad, 0xa1, 0xd5, 0xaf, 0xa3, 0xd5, 0xad, 0xa1, 0xd5, 0xb0, 0xa3, 0xd6, 0xad, 0xa0, 0xd6, 0xad, 0xa0, 0xd5, 0xb0, 0xa3, 0xd7, 0xb1, 0xa5, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa7, 0xd7, 0xb1, 0xa5, 0xd7, 0xb1, 0xa5, 0xd8, 0xb3, 0xa8, 0xd8, 0xb5, 0xaa, 0xda, 0xb3, 0xa8, 0xda, 0xb3, 0xa8, 0xd8, 0xb3, 0xa8, 0xd8, 0xb5, 0xaa, 0xda, 0xb3, 0xa8, 0xd8, 0xb3, 0xa8, 0xdb, 0xb7, 0xad, 0xda, 0xb8, 0xae, 0xdb, 0xb9, 0xad, 0xdb, 0xb9, 0xad, 0xdb, 0xb7, 0xad, 0xdd, 0xba, 0xb1, 0xdd, 0xbb, 0xb1, 0xdd, 0xbd, 0xb1, 0xdd, 0xbf, 0xb3, 0xdd, 0xbd, 0xb3, 0xdf, 0xc1, 0xb7, 0xdf, 0xc0, 0xb5, 0xdf, 0xbf, 0xb5, 0xe1, 0xc3, 0xb9, 0xe1, 0xc3, 0xbb, 0xe1, 0xc5, 0xbb, 0xe2, 0xc7, 0xbe, 0xe1, 0xc5, 0xbd, 0xe4, 0xcb, 0xc2, 0xe3, 0xca, 0xc1, 0xe3, 0xcb, 0xc3, 0xe6, 0xce, 0xc6, 0xe6, 0xd0, 0xc6, 0xe8, 0xd1, 0xca, 0xe8, 0xd1, 0xca, 0xe8, 0xd0, 0xca, 0xe8, 0xcf, 0xca, 0xb7, 0x7d, 0x69, 0xb1, 0x77, 0x63, 0xac, 0x73, 0x5c, 0xa6, 0x69, 0x56, 0x9c, 0x67, 0x54, 0x93, 0x62, 0x51, 0x88, 0x60, 0x50, 0x9e, 0xe4, 0xa3, 0x87, 0x0, 0x0, 0x0, 0x3a, 0x74, 0x52, 0x4e, 0x53, 0x32, 0x2f, 0x30, 0x33, 0x38, 0x40, 0x32, 0x2f, 0x2f, 0x2f, 0x31, 0x33, 0x33, 0x33, 0x36, 0x38, 0x31, 0x2f, 0x30, 0x31, 0x33, 0x33, 0x35, 0x2f, 0x2f, 0x2f, 0x30, 0x32, 0x33, 0x33, 0x33, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x32, 0x2f, 0x2f, 0x2f, 0xb8, 0xf, 0x95, 0x41, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x4d, 0xc9, 0x1, 0xa, 0x2, 0x31, 0xc, 0x44, 0xd1, 0xff, 0xdb, 0xec, 0x2a, 0x1e, 0x48, 0xc0, 0xfb, 0x7b, 0x2c, 0x3b, 0x96, 0x2c, 0x88, 0x3, 0x19, 0x78, 0x19, 0x5f, 0xfc, 0xa7, 0x7c, 0xb, 0x78, 0xd5, 0xb3, 0x3c, 0x45, 0xf5, 0xea, 0xf2, 0x21, 0xb4, 0xc2, 0xd8, 0xbc, 0x19, 0xcd, 0xb0, 0xab, 0xbc, 0x83, 0xa4, 0x1f, 0x6c, 0x1e, 0x82, 0xca, 0x67, 0x2c, 0x2d, 0xe, 0x25, 0x31, 0x67, 0x6a, 0x51, 0x99, 0xc0, 0xc8, 0xbe, 0x35, 0xcc, 0x45, 0xc, 0xca, 0xdc, 0x1c, 0x6, 0x70, 0x8f, 0xa1, 0xd7, 0x36, 0x13, 0xe8, 0xb5, 0x6d, 0x18, 0x6b, 0xb3, 0xf8, 0x65, 0xe6, 0xb, 0x36, 0x5c, 0x24, 0xde, 0x86, 0x96, 0x3a, 0xaf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_cursor_unfocus_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0xe, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x96, 0xdd, 0xe3, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0xc0, 0xb5, 0xb2, 0xbf, 0xb4, 0xb1, 0xc0, 0xb5, 0xb2, 0xbf, 0xb4, 0xb1, 0xbf, 0xb5, 0xb1, 0xc0, 0xb5, 0xb2, 0xc3, 0xb8, 0xb5, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc5, 0xba, 0xb7, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xba, 0xb7, 0xc5, 0xba, 0xb7, 0xc6, 0xbd, 0xba, 0xc7, 0xbe, 0xbb, 0xc7, 0xbe, 0xbb, 0xc6, 0xbd, 0xba, 0xc7, 0xbe, 0xbb, 0xc6, 0xbd, 0xba, 0xca, 0xc1, 0xbe, 0xca, 0xc1, 0xbe, 0xcd, 0xc4, 0xc1, 0xcd, 0xc6, 0xc3, 0xd0, 0xc9, 0xc6, 0xcf, 0xc8, 0xc5, 0xd2, 0xcb, 0xc8, 0xd3, 0xcb, 0xc9, 0xd3, 0xcc, 0xc9, 0xd5, 0xcd, 0xcb, 0xd4, 0xcc, 0xca, 0xd7, 0xd1, 0xcf, 0xd6, 0xd0, 0xce, 0xda, 0xd4, 0xd2, 0xdd, 0xd7, 0xd5, 0xdd, 0xd7, 0xd5, 0x9a, 0x8b, 0x86, 0x94, 0x85, 0x80, 0x8e, 0x80, 0x7a, 0x88, 0x79, 0x74, 0x81, 0x75, 0x6f, 0x7a, 0x6f, 0x6a, 0x73, 0x69, 0x65, 0x0, 0x0, 0x0, 0xfb, 0x87, 0x71, 0x8e, 0x0, 0x0, 0x0, 0x2b, 0x74, 0x52, 0x4e, 0x53, 0x32, 0x2f, 0x30, 0x33, 0x38, 0x40, 0x32, 0x2f, 0x2f, 0x31, 0x33, 0x33, 0x36, 0x38, 0x31, 0x2f, 0x30, 0x31, 0x33, 0x33, 0x35, 0x2f, 0x2f, 0x30, 0x32, 0x33, 0x33, 0x2f, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x32, 0x2f, 0x82, 0xd8, 0x8a, 0x2f, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x32, 0x40, 0xd2, 0x4c, 0xc8, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x4, 0x4e, 0x1d, 0x2, 0xaf, 0x0, 0x0, 0x0, 0x82, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x55, 0xcf, 0xe9, 0x16, 0xc1, 0x30, 0x14, 0x4, 0xe0, 0xb1, 0x96, 0x16, 0x25, 0x34, 0x84, 0x26, 0x2d, 0xaa, 0xb6, 0xac, 0xef, 0xff, 0x72, 0x6e, 0x38, 0xe, 0xfd, 0xe6, 0xce, 0x99, 0xdf, 0x17, 0xcf, 0xe, 0x68, 0xf4, 0x48, 0x7f, 0x40, 0x86, 0x23, 0xd, 0x33, 0x4e, 0x92, 0x64, 0x32, 0x4d, 0xb3, 0x2c, 0x9d, 0xcd, 0xd, 0xec, 0x22, 0xcf, 0xf3, 0xe5, 0x8a, 0xb1, 0x35, 0x63, 0x1b, 0xb, 0x57, 0xf0, 0x82, 0x73, 0xbe, 0xdd, 0x9, 0x21, 0xf6, 0xe, 0xfe, 0xf0, 0x55, 0x96, 0xa5, 0x47, 0x90, 0x91, 0x52, 0x4a, 0x2a, 0x29, 0x3, 0x42, 0x55, 0x93, 0xaa, 0x8e, 0x53, 0x5, 0xf8, 0x23, 0x39, 0x51, 0xcf, 0x14, 0xf, 0xd7, 0x44, 0x97, 0xf7, 0x35, 0xe, 0xb6, 0xbd, 0xb6, 0x1f, 0xb4, 0x16, 0xe6, 0xf6, 0xc7, 0x40, 0xdf, 0x1f, 0x3f, 0x1a, 0xdd, 0x17, 0x5e, 0xc0, 0x36, 0x18, 0x83, 0x7f, 0x54, 0x76, 0x87, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0xe, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x96, 0xdd, 0xe3, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0xc0, 0xb5, 0xb2, 0xbf, 0xb4, 0xb1, 0xc0, 0xb5, 0xb2, 0xbf, 0xb4, 0xb1, 0xbf, 0xb5, 0xb1, 0xc0, 0xb5, 0xb2, 0xc3, 0xb8, 0xb5, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc2, 0xb7, 0xb4, 0xc3, 0xb8, 0xb5, 0xc5, 0xba, 0xb7, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xbc, 0xb9, 0xc5, 0xba, 0xb7, 0xc5, 0xba, 0xb7, 0xc6, 0xbd, 0xba, 0xc7, 0xbe, 0xbb, 0xc7, 0xbe, 0xbb, 0xc6, 0xbd, 0xba, 0xc7, 0xbe, 0xbb, 0xc6, 0xbd, 0xba, 0xca, 0xc1, 0xbe, 0xca, 0xc1, 0xbe, 0xcd, 0xc4, 0xc1, 0xcd, 0xc6, 0xc3, 0xd0, 0xc9, 0xc6, 0xcf, 0xc8, 0xc5, 0xd2, 0xcb, 0xc8, 0xd3, 0xcb, 0xc9, 0xd3, 0xcc, 0xc9, 0xd5, 0xcd, 0xcb, 0xd4, 0xcc, 0xca, 0xd7, 0xd1, 0xcf, 0xd6, 0xd0, 0xce, 0xda, 0xd4, 0xd2, 0xdd, 0xd7, 0xd5, 0xdd, 0xd7, 0xd5, 0x9a, 0x8b, 0x86, 0x94, 0x85, 0x80, 0x8e, 0x80, 0x7a, 0x88, 0x79, 0x74, 0x81, 0x75, 0x6f, 0x7a, 0x6f, 0x6a, 0x73, 0x69, 0x65, 0x9a, 0x51, 0xd2, 0xe3, 0x0, 0x0, 0x0, 0x2b, 0x74, 0x52, 0x4e, 0x53, 0x32, 0x2f, 0x30, 0x33, 0x38, 0x40, 0x32, 0x2f, 0x2f, 0x31, 0x33, 0x33, 0x36, 0x38, 0x31, 0x2f, 0x30, 0x31, 0x33, 0x33, 0x35, 0x2f, 0x2f, 0x30, 0x32, 0x33, 0x33, 0x2f, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x32, 0x2f, 0x82, 0xd8, 0x8a, 0x2f, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0xd1, 0x66, 0x40, 0x6, 0x2c, 0x8c, 0x57, 0x0, 0x2d, 0xc9, 0x87, 0x15, 0x4, 0x51, 0x8, 0x42, 0xd1, 0x7, 0x13, 0x3b, 0xd9, 0xfe, 0x1b, 0x1c, 0x16, 0xcf, 0xf, 0xa6, 0xab, 0x6a, 0xd3, 0x2a, 0xbf, 0x53, 0xb7, 0x66, 0xa8, 0x63, 0xe9, 0xd4, 0xbb, 0x88, 0x82, 0xcb, 0x47, 0xd9, 0xb, 0x54, 0xde, 0xec, 0x57, 0x97, 0xde, 0x63, 0x94, 0x12, 0xab, 0xec, 0xec, 0x56, 0xce, 0x69, 0x28, 0xc8, 0x9f, 0xbf, 0x9c, 0x39, 0xd8, 0x16, 0x47, 0x69, 0x65, 0xc, 0xe, 0xc3, 0x8e, 0xeb, 0x69, 0x28, 0x1, 0x0, 0xb0, 0xae, 0x0, 0x0, 0x90, 0x3f, 0xa6, 0x5b, 0x1b, 0xad, 0x12, 0x69, 0xd7, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_title_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x23, 0x25, 0x4c, 0x4a, 0x4e, 0x1, 0xf9, 0x98, 0x2e, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xc, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x20, 0xd, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0xc7, 0xaa, 0x85, 0x8e, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x23, 0x25, 0x4c, 0x4a, 0x4e, 0x1, 0xf9, 0x98, 0x2e, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char tree_title_pressed_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x36, 0x34, 0x36, 0x4c, 0x4a, 0x4e, 0x14, 0xd7, 0x5b, 0xf8, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xc, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x20, 0xd, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0xc7, 0xaa, 0x85, 0x8e, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x1, 0x3, 0x0, 0x0, 0x0, 0x25, 0x3d, 0x6d, 0x22, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x36, 0x34, 0x36, 0x4c, 0x4a, 0x4e, 0x14, 0xd7, 0x5b, 0xf8, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0x0, 0x0, 0x0, 0x30, 0x0, 0x1, 0x6e, 0xa6, 0xf, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char unchecked_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x36, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0xff, 0xff, 0xff, 0xf7, 0x93, 0x46, 0x7a, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x11, 0xe2, 0xb5, 0x3d, 0xba, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x53, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0xb5, 0xcf, 0xc1, 0x12, 0x80, 0x20, 0x8, 0x45, 0x51, 0x5, 0x14, 0xd, 0x5, 0xff, 0xff, 0x6b, 0x23, 0x27, 0x67, 0x9a, 0x6c, 0xdb, 0x5d, 0x9e, 0xd, 0x8f, 0x10, 0xb6, 0x22, 0x20, 0xa5, 0x19, 0x21, 0x44, 0x7, 0xc8, 0x2c, 0x6d, 0x26, 0x9c, 0xc1, 0x1, 0xb9, 0xab, 0xcd, 0xb4, 0x33, 0x3a, 0x90, 0xe8, 0xb8, 0x53, 0x21, 0x87, 0xd4, 0x6c, 0x81, 0xb5, 0xf4, 0x17, 0x6c, 0x67, 0x9f, 0xc3, 0xca, 0x35, 0xc, 0x6a, 0x59, 0xd3, 0x8f, 0xa, 0x5f, 0xcf, 0xbd, 0x3a, 0x1, 0x93, 0xe2, 0x8, 0xa4, 0xb1, 0xeb, 0xd3, 0x56, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x33, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x38, 0x37, 0x40, 0x20, 0x20, 0x24, 0x20, 0x20, 0x24, 0x38, 0x36, 0x40, 0x20, 0x20, 0x25, 0x1e, 0x1e, 0x22, 0x1f, 0x1f, 0x23, 0x20, 0x20, 0x24, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x23, 0xc3, 0x49, 0x39, 0x0, 0x0, 0x0, 0xb, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x50, 0x66, 0x68, 0xb4, 0xfa, 0xfb, 0xb4, 0xfa, 0xa4, 0x7f, 0xe1, 0x5a, 0x0, 0x0, 0x0, 0x4f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xb5, 0xcf, 0x45, 0x2, 0x80, 0x40, 0x8, 0x0, 0x40, 0x97, 0x66, 0xfb, 0xff, 0x9f, 0xb5, 0xdb, 0xb3, 0x73, 0xa4, 0x19, 0xbe, 0x2, 0x20, 0xf1, 0x8a, 0x10, 0xc2, 0x1c, 0x0, 0xd1, 0x94, 0x57, 0x49, 0x5, 0xe6, 0x0, 0x6a, 0xa9, 0x6d, 0x55, 0x8b, 0xe2, 0x1c, 0xa0, 0x54, 0xfb, 0xae, 0x26, 0x9a, 0x3, 0x9c, 0xdb, 0x11, 0x68, 0x99, 0xff, 0xa, 0x7c, 0xd6, 0xde, 0xf, 0x33, 0x9c, 0x3, 0xe0, 0x76, 0x9c, 0x1e, 0x1d, 0xbe, 0xcf, 0x7d, 0x4c, 0x93, 0xe2, 0x8, 0xa4, 0x66, 0x3c, 0xec, 0xed, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char updown_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x2b, 0x8a, 0x3e, 0x7d, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0xa6, 0x49, 0x44, 0x41, 0x54, 0x28, 0x91, 0xa5, 0x90, 0x21, 0xe, 0xc2, 0x50, 0x10, 0x44, 0xdf, 0x27, 0x9b, 0x10, 0x2e, 0xc0, 0x9, 0xd6, 0x81, 0xe3, 0x6, 0xe0, 0xa0, 0xd7, 0x2d, 0xae, 0x18, 0x24, 0x95, 0xc5, 0x7d, 0x51, 0xdd, 0xb, 0x60, 0xf6, 0x67, 0x31, 0xfb, 0x49, 0xc1, 0x20, 0x3a, 0x6a, 0x93, 0x99, 0x9d, 0xcc, 0xc, 0x2c, 0x45, 0xaa, 0x47, 0xce, 0x79, 0x2b, 0x22, 0x2d, 0x80, 0x99, 0x5d, 0x54, 0x75, 0xfa, 0x8, 0x82, 0xec, 0x80, 0x7d, 0xe8, 0x7, 0x33, 0x3b, 0xa9, 0xea, 0x94, 0x82, 0xbc, 0x1, 0x3b, 0x60, 0x8, 0xc1, 0x1e, 0x78, 0x9a, 0xd9, 0x51, 0xc2, 0x76, 0x57, 0xbf, 0x0, 0xaa, 0x9b, 0x88, 0xb4, 0x2b, 0x77, 0x7f, 0x1, 0x7d, 0xb5, 0x54, 0xd5, 0x29, 0x84, 0x7d, 0x70, 0x4b, 0x6b, 0x8e, 0xe3, 0x78, 0x7, 0x36, 0x66, 0x76, 0xae, 0xd5, 0x22, 0xf8, 0x15, 0x78, 0x89, 0xbb, 0xaf, 0x53, 0x4a, 0x7, 0x11, 0xe9, 0x72, 0xce, 0x5f, 0x21, 0xdd, 0xfd, 0x21, 0xa5, 0x94, 0x66, 0x96, 0xba, 0x9b, 0xd5, 0x1c, 0x4a, 0x29, 0xcd, 0xff, 0xa1, 0x7e, 0xa6, 0xbe, 0xc6, 0xd4, 0x9f, 0x3c, 0xcb, 0xf1, 0x6, 0x8e, 0x4e, 0x65, 0x44, 0x6f, 0x74, 0x5c, 0xa1, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0x81, 0x83, 0xf6, 0xf6, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x8d, 0x4e, 0xb5, 0x1, 0xc0, 0x30, 0xc, 0xf3, 0x15, 0xfe, 0xff, 0x96, 0x64, 0xa, 0x6c, 0xca, 0x56, 0xd2, 0x25, 0x65, 0xe6, 0xc8, 0x8b, 0x49, 0x20, 0x79, 0x28, 0x95, 0x81, 0xa1, 0xd4, 0x7d, 0x4, 0xbb, 0xa1, 0x50, 0xea, 0x3c, 0xa6, 0x71, 0x98, 0x96, 0x69, 0x58, 0x31, 0xcc, 0xb7, 0xe5, 0x2f, 0x48, 0x63, 0x26, 0xf6, 0xa2, 0xd4, 0x18, 0xf9, 0x7, 0x2d, 0xe3, 0x46, 0x89, 0xb4, 0xd2, 0xf8, 0xa3, 0x68, 0xe3, 0xd7, 0x14, 0x20, 0xe6, 0xc3, 0x3d, 0xd8, 0xca, 0x5e, 0x94, 0x32, 0xd0, 0x3, 0x91, 0xba, 0x5f, 0x1b, 0x4a, 0x9b, 0x12, 0x62, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vseparator_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0xc, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xff, 0xff, 0xff, 0x64, 0x6c, 0x1, 0xd2, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x3, 0x11, 0xc, 0x4c, 0xf2, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x10, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x80, 0x81, 0xac, 0x95, 0xc, 0x48, 0x0, 0x0, 0xe, 0x79, 0x1, 0x14, 0xa1, 0xc9, 0x59, 0x2, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x2, 0x3, 0x0, 0x0, 0x0, 0xb9, 0x61, 0x56, 0x18, 0x0, 0x0, 0x0, 0x9, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x73, 0x9b, 0xaa, 0xce, 0xdc, 0xe1, 0xeb, 0x64, 0x9a, 0x78, 0x0, 0x0, 0x0, 0x3, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xb3, 0xb3, 0x67, 0xf6, 0xdb, 0x93, 0x0, 0x0, 0x0, 0xf, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x83, 0xac, 0x95, 0xc, 0x48, 0x0, 0x0, 0xe, 0x79, 0x1, 0x14, 0x17, 0x9a, 0x55, 0x26, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vslider_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x54, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x34, 0x33, 0x3a, 0x2d, 0x2c, 0x32, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x40, 0x3e, 0x4a, 0x20, 0x20, 0x24, 0x34, 0x33, 0x3a, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x1e, 0x1e, 0x23, 0x23, 0x23, 0x27, 0x2d, 0x2c, 0x32, 0x1f, 0x1f, 0x23, 0xff, 0xff, 0xff, 0x3, 0x35, 0xf1, 0x5f, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x4, 0x1a, 0x40, 0x5d, 0x19, 0x28, 0x96, 0xf0, 0xfd, 0x94, 0x95, 0xfc, 0x93, 0xfc, 0xc0, 0x0, 0xb4, 0xa, 0x5f, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1b, 0x2, 0x60, 0xd4, 0xa4, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x9d, 0xcf, 0x39, 0x2, 0x80, 0x20, 0x10, 0x3, 0xc0, 0xe5, 0x90, 0x43, 0x50, 0x94, 0x1b, 0xfe, 0xff, 0x50, 0x5, 0x69, 0x94, 0xce, 0x94, 0x53, 0xec, 0x26, 0x0, 0x0, 0x88, 0x2e, 0x8c, 0x73, 0x26, 0x28, 0x82, 0x1e, 0x44, 0xe5, 0xaa, 0xb4, 0x56, 0x9b, 0x1c, 0x82, 0xc4, 0x6e, 0x9c, 0xf7, 0xce, 0x1c, 0x62, 0x0, 0x53, 0x2e, 0xc4, 0x18, 0x9c, 0x62, 0x3, 0xb8, 0xf6, 0xf1, 0x8e, 0x3f, 0xf9, 0x3, 0xd8, 0xa6, 0xdc, 0x20, 0x27, 0x8b, 0x3b, 0x90, 0x52, 0x43, 0x83, 0x50, 0xb, 0xf9, 0xb, 0xd3, 0xd1, 0xe9, 0xed, 0x5c, 0x6c, 0xaa, 0xfe, 0x1d, 0xf7, 0x9e, 0x7f, 0x1, 0x89, 0x5c, 0xa, 0x6b, 0x1f, 0xe5, 0xca, 0x60, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x51, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x4d, 0x4b, 0x59, 0x34, 0x33, 0x3a, 0x2d, 0x2c, 0x32, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x4d, 0x4b, 0x59, 0x3f, 0x3e, 0x49, 0x2a, 0x29, 0x2f, 0x20, 0x20, 0x24, 0x3f, 0x3e, 0x49, 0x3f, 0x3e, 0x49, 0x1f, 0x1f, 0x24, 0x40, 0x3e, 0x4a, 0x20, 0x20, 0x24, 0x34, 0x33, 0x3a, 0x20, 0x20, 0x25, 0x22, 0x22, 0x27, 0x23, 0x23, 0x28, 0x25, 0x25, 0x2a, 0x1e, 0x1e, 0x23, 0x23, 0x23, 0x27, 0x2d, 0x2c, 0x32, 0x1f, 0x1f, 0x23, 0x30, 0x7, 0x9c, 0xfe, 0x0, 0x0, 0x0, 0x13, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x4, 0x1a, 0x40, 0x5d, 0x19, 0x28, 0x96, 0xf0, 0xfd, 0x94, 0x95, 0xfc, 0x93, 0xfc, 0xc0, 0x0, 0xb4, 0xa, 0x5f, 0x0, 0x0, 0x0, 0x5f, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x2c, 0xc8, 0x83, 0x11, 0x0, 0x30, 0x0, 0xc0, 0xc0, 0xda, 0xf6, 0xfe, 0x8b, 0xd6, 0x39, 0xe6, 0xc1, 0xe, 0x12, 0xca, 0x38, 0x67, 0x82, 0xc0, 0x73, 0xe7, 0xa5, 0xd2, 0xc6, 0x68, 0x2b, 0xbf, 0x40, 0xe1, 0x7c, 0x2e, 0x25, 0xfb, 0x20, 0x3e, 0x30, 0x9d, 0x6b, 0x6b, 0x35, 0x6b, 0xf6, 0x81, 0x9b, 0xd2, 0x76, 0x25, 0xf2, 0x7, 0x28, 0xf5, 0x71, 0x60, 0xf4, 0x84, 0x2e, 0xe0, 0x35, 0x49, 0x29, 0x51, 0x90, 0x80, 0xa8, 0x94, 0x24, 0x33, 0x19, 0x2, 0x18, 0x86, 0xa2, 0x5b, 0x8b, 0xe9, 0x30, 0x4c, 0xa7, 0x63, 0x7a, 0xe, 0xc3, 0xfb, 0x0, 0x89, 0x5c, 0xa, 0x6b, 0x4f, 0x78, 0xac, 0x83, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vslider_grabber_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xb7, 0xff, 0x88, 0x5, 0x1d, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe1, 0x1, 0x12, 0x1, 0x36, 0x8, 0x50, 0xb9, 0xa7, 0x53, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0xf6, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0xbd, 0x90, 0xb1, 0x4a, 0x42, 0x51, 0x0, 0x86, 0xbf, 0x73, 0x8e, 0x71, 0xe5, 0x9a, 0x5c, 0x41, 0xd0, 0x66, 0x6b, 0x33, 0x1c, 0x7c, 0x80, 0xa0, 0xa5, 0x17, 0x8, 0xa2, 0x2d, 0x84, 0xf0, 0x1, 0xa2, 0x25, 0xf1, 0x9, 0x9a, 0x1c, 0xda, 0x5b, 0xb2, 0x47, 0xa8, 0xa5, 0xc1, 0xa0, 0x51, 0x88, 0xa2, 0x29, 0xa, 0xc1, 0x84, 0x8, 0x43, 0xf4, 0x96, 0x17, 0xcf, 0xed, 0xde, 0x73, 0x9c, 0xcc, 0x5c, 0xda, 0xea, 0x9f, 0x3f, 0xfe, 0x9f, 0xef, 0x87, 0x3f, 0x8f, 0x0, 0x40, 0xe1, 0xe2, 0x91, 0x42, 0x10, 0x32, 0xe6, 0x3, 0x8d, 0xc1, 0xce, 0x1, 0x45, 0xb6, 0xba, 0xbb, 0xba, 0xed, 0x95, 0x8c, 0xd0, 0x7d, 0xff, 0xe1, 0xee, 0xe2, 0xb6, 0xdd, 0x79, 0x61, 0xc4, 0xd7, 0xc, 0x48, 0x57, 0x2b, 0xeb, 0xb5, 0x28, 0xaf, 0x1, 0xc5, 0x12, 0x4e, 0xac, 0x7b, 0x6f, 0x57, 0x27, 0x8d, 0xcf, 0xe, 0x1, 0x56, 0x1, 0xb9, 0x9d, 0xba, 0x28, 0x6, 0x18, 0xc, 0x31, 0x21, 0x5a, 0xda, 0x4c, 0xb6, 0xbc, 0xb9, 0x35, 0x7c, 0xea, 0xbd, 0x13, 0x4a, 0x20, 0xe5, 0x95, 0xf4, 0x6c, 0x12, 0x30, 0x84, 0xf8, 0x44, 0x6b, 0xfb, 0xcd, 0x83, 0x3d, 0x1c, 0xf9, 0x8b, 0x80, 0x4a, 0xba, 0x88, 0x4, 0x30, 0x1e, 0xdd, 0x3b, 0x1b, 0xf1, 0x77, 0x87, 0x24, 0x81, 0x8b, 0x79, 0x3e, 0x3b, 0x6a, 0x5d, 0x33, 0x51, 0x80, 0x2d, 0x38, 0x2b, 0x65, 0xb5, 0x6c, 0x91, 0x28, 0x92, 0xa4, 0xad, 0xec, 0x76, 0xcf, 0x8f, 0xf, 0x1f, 0xdb, 0xc, 0x31, 0xb, 0x9a, 0xb1, 0xd0, 0x3, 0xfb, 0xda, 0x3a, 0xbd, 0xbc, 0x89, 0xfa, 0xf8, 0x73, 0xcd, 0x9f, 0x47, 0x45, 0x4, 0xf8, 0x4, 0x18, 0xfe, 0x2f, 0x53, 0x8, 0x62, 0x5c, 0xcf, 0x1f, 0x5f, 0xcb, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xbc, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0xd2, 0xf2, 0x3a, 0xf, 0xce, 0x78, 0x3f, 0xed, 0x43, 0xff, 0xed, 0xc6, 0xf5, 0x41, 0xa9, 0x8a, 0x86, 0xc, 0x22, 0xc, 0xac, 0x8, 0x5, 0xbc, 0x69, 0x79, 0x93, 0x5e, 0xf4, 0xfd, 0x6f, 0x7, 0xc2, 0xae, 0xff, 0xfd, 0xff, 0xa7, 0xfd, 0xe9, 0x7f, 0x50, 0x31, 0x87, 0x47, 0x87, 0x81, 0x1b, 0x66, 0x8e, 0x4c, 0xe7, 0xc1, 0xbe, 0xff, 0x2d, 0xff, 0x9b, 0xa1, 0xb0, 0xf5, 0x7f, 0xe7, 0xff, 0xc9, 0xff, 0x27, 0xdc, 0xb6, 0xf0, 0x7, 0x9a, 0xc3, 0x2, 0x52, 0xa0, 0x3e, 0xe3, 0x7d, 0x3b, 0x54, 0x12, 0xa1, 0xa8, 0xe7, 0xff, 0x9c, 0x2f, 0x45, 0xc5, 0xc, 0xdc, 0xf8, 0x14, 0x7c, 0xaf, 0xaa, 0x65, 0xe0, 0xc1, 0x69, 0xc5, 0xc4, 0x3b, 0xe, 0xa1, 0xc, 0x62, 0xc, 0x2c, 0x68, 0x8e, 0xec, 0xf8, 0xdf, 0xfb, 0x7f, 0xda, 0xbf, 0x89, 0xf, 0x4a, 0xa6, 0xf2, 0xe8, 0x0, 0x75, 0x33, 0xa2, 0x79, 0x73, 0xea, 0x87, 0xbe, 0x7b, 0xbd, 0x47, 0x7d, 0x53, 0x58, 0x34, 0x18, 0x84, 0x19, 0x58, 0xb1, 0x7, 0x94, 0xa, 0x83, 0x14, 0x50, 0x27, 0x13, 0x3, 0x3d, 0x1, 0x0, 0x79, 0xc3, 0x79, 0x54, 0x19, 0x56, 0x3b, 0x28, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vslider_grabber_disabled_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe1, 0x7, 0xa, 0x13, 0x2e, 0x39, 0x86, 0x33, 0xc2, 0xfe, 0x0, 0x0, 0x0, 0x1d, 0x69, 0x54, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x64, 0x2e, 0x65, 0x7, 0x0, 0x0, 0x0, 0xb7, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0xa0, 0x13, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0x8c, 0xf3, 0x5c, 0xf, 0xfa, 0xbc, 0xf7, 0xfe, 0xe0, 0x71, 0xdb, 0x71, 0xbd, 0x66, 0xaa, 0xa0, 0x21, 0x83, 0x8, 0x3, 0x2b, 0x42, 0x1, 0xaf, 0x71, 0x9e, 0xe7, 0xb, 0xf7, 0xff, 0x2e, 0x40, 0xe8, 0xfa, 0xdf, 0xe3, 0xbf, 0xf7, 0x1f, 0x8f, 0x7, 0x36, 0x73, 0xd8, 0x74, 0x18, 0xb8, 0x61, 0xe6, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x87, 0xce, 0x40, 0x65, 0x5e, 0xff, 0x3d, 0x6e, 0xcb, 0xf8, 0x3, 0xcd, 0x61, 0x1, 0x29, 0x50, 0xf7, 0x79, 0xef, 0x82, 0xa4, 0x0, 0xa2, 0xc8, 0xed, 0xbf, 0xdf, 0x17, 0xcb, 0x62, 0xa0, 0x29, 0x78, 0x14, 0x7c, 0xb7, 0xad, 0x65, 0xe0, 0xc1, 0x69, 0x85, 0xe7, 0x1d, 0x85, 0x50, 0x6, 0x31, 0x88, 0x15, 0x48, 0x8e, 0x74, 0xf9, 0xef, 0xfe, 0xdf, 0xfb, 0x9f, 0xe7, 0x3, 0xab, 0xa9, 0x40, 0x47, 0xf2, 0xc0, 0x1c, 0x89, 0xe4, 0x4d, 0xf7, 0x7b, 0xee, 0x47, 0xd5, 0x53, 0x98, 0x34, 0x18, 0x84, 0x91, 0xbd, 0x89, 0x1c, 0x50, 0x2a, 0xc, 0x52, 0x40, 0x9d, 0x4c, 0xc, 0xf4, 0x4, 0x0, 0xf1, 0x9, 0x63, 0x9b, 0x3e, 0x2a, 0x19, 0x52, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xb4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x17, 0x60, 0x66, 0xe0, 0x65, 0x90, 0x61, 0x50, 0x67, 0xd0, 0x60, 0x50, 0x62, 0x10, 0x67, 0xe0, 0x2, 0xf2, 0x19, 0x51, 0xa5, 0xc5, 0x8c, 0xf3, 0x5c, 0xf, 0xfa, 0xbc, 0xf7, 0xfe, 0xe0, 0x71, 0xdb, 0x71, 0xbd, 0x66, 0xaa, 0xa0, 0x21, 0x83, 0x8, 0x3, 0x2b, 0x42, 0x1, 0xaf, 0x71, 0x9e, 0xe7, 0xb, 0xf7, 0xff, 0x2e, 0x40, 0xe8, 0xfa, 0xdf, 0xe3, 0xbf, 0xf7, 0x1f, 0x8f, 0x7, 0x36, 0x73, 0xd8, 0x74, 0x18, 0xb8, 0x61, 0xe6, 0xc8, 0xb8, 0x1e, 0x74, 0xff, 0xef, 0x4, 0x87, 0xce, 0x40, 0x65, 0x5e, 0xff, 0x3d, 0x6e, 0xcb, 0xf8, 0x3, 0xcd, 0x61, 0x1, 0x29, 0x50, 0xf7, 0x79, 0xef, 0x2, 0x95, 0x44, 0x28, 0x72, 0xfb, 0xef, 0xf7, 0xc5, 0xb2, 0x98, 0x81, 0x1b, 0x9f, 0x82, 0xef, 0xb6, 0xb5, 0xc, 0x3c, 0x38, 0xad, 0xf0, 0xbc, 0xa3, 0x10, 0xca, 0x20, 0xc6, 0xc0, 0x82, 0xec, 0x48, 0x30, 0x74, 0xff, 0xef, 0xfd, 0xcf, 0xf3, 0x81, 0xd5, 0x54, 0x36, 0x1d, 0xa0, 0x6e, 0x46, 0xc, 0x6f, 0xba, 0xdf, 0x73, 0x3f, 0xaa, 0x9e, 0xc2, 0xa4, 0xc1, 0x20, 0xcc, 0xc0, 0x8a, 0x3d, 0xa0, 0x54, 0x18, 0xa4, 0x80, 0x3a, 0x99, 0x18, 0xe8, 0x9, 0x0, 0xf1, 0x9, 0x63, 0x9b, 0x53, 0x7f, 0x6d, 0x9b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vslider_grabber_hl_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0xc3, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0x2a, 0x29, 0x3a, 0x69, 0x69, 0x5b, 0xa6, 0xa5, 0x61, 0xb3, 0xbc, 0x63, 0xb7, 0xc8, 0x65, 0xbb, 0xca, 0x60, 0xaf, 0xb1, 0x48, 0x83, 0x83, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf, 0xf, 0x55, 0x9b, 0x9a, 0x60, 0xb2, 0xbd, 0x5e, 0xb1, 0xcd, 0x61, 0xb3, 0xc2, 0x0, 0x0, 0x0, 0x27, 0x48, 0x47, 0x62, 0xb4, 0xbd, 0x51, 0x93, 0x92, 0x68, 0xc0, 0xcf, 0x0, 0x0, 0x0, 0x56, 0x9d, 0x9c, 0x68, 0xc1, 0xcf, 0x2d, 0x52, 0x52, 0x63, 0xb7, 0xbf, 0x52, 0x96, 0x95, 0x62, 0xb3, 0xbf, 0x5e, 0xb0, 0xcd, 0x0, 0x0, 0x0, 0x3, 0x5, 0x5, 0x36, 0x63, 0x63, 0x63, 0xb4, 0xb6, 0x60, 0xb1, 0xbc, 0x63, 0xb7, 0xc7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0xa3, 0xc8, 0x4f, 0x98, 0xc4, 0x4b, 0x93, 0xc2, 0x4c, 0x94, 0xc2, 0x54, 0xa2, 0xc8, 0x5a, 0xab, 0xcb, 0x4e, 0x97, 0xc4, 0x49, 0x8f, 0xc0, 0x47, 0x8c, 0xbf, 0x48, 0x8e, 0xc0, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0x5a, 0xac, 0xcc, 0x53, 0x9f, 0xc7, 0x4d, 0x96, 0xc3, 0x4b, 0x92, 0xc2, 0xff, 0xff, 0xff, 0x76, 0xbd, 0x27, 0x7a, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe1, 0x1, 0x12, 0x1, 0x36, 0x11, 0x34, 0xd2, 0xf, 0x93, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x48, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3, 0x63, 0x60, 0xa0, 0x12, 0x10, 0x14, 0xe0, 0xe7, 0xe3, 0x45, 0xe2, 0x4b, 0x9a, 0x18, 0x1b, 0x19, 0x1a, 0x48, 0x88, 0x8b, 0xc1, 0xe4, 0x4d, 0x2c, 0x2d, 0x80, 0xc0, 0xdc, 0xcc, 0x54, 0x6, 0x22, 0x20, 0x60, 0x6c, 0x1, 0x1, 0xe6, 0x56, 0x72, 0x68, 0x2, 0xd6, 0x8a, 0xa8, 0x5a, 0x6c, 0x94, 0x11, 0x86, 0xda, 0xdb, 0xd9, 0xaa, 0xa9, 0xaa, 0x20, 0x59, 0xab, 0xa3, 0xad, 0xc5, 0x40, 0x3d, 0x0, 0x0, 0xbf, 0x8e, 0xc, 0xed, 0xed, 0xc7, 0x67, 0x72, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x6c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x5b, 0xa6, 0xa5, 0x61, 0xb3, 0xbc, 0x63, 0xb7, 0xc8, 0x65, 0xbb, 0xca, 0x60, 0xaf, 0xb1, 0x55, 0x9b, 0x9a, 0x60, 0xb2, 0xbd, 0x5e, 0xb1, 0xcd, 0x61, 0xb3, 0xc2, 0x62, 0xb4, 0xbd, 0x68, 0xc0, 0xcf, 0x68, 0xc1, 0xcf, 0x63, 0xb7, 0xbf, 0x52, 0x96, 0x95, 0x62, 0xb3, 0xbf, 0x5e, 0xb0, 0xcd, 0x63, 0xb4, 0xb6, 0x60, 0xb1, 0xbc, 0x63, 0xb7, 0xc7, 0x55, 0xa3, 0xc8, 0x4f, 0x98, 0xc4, 0x4b, 0x93, 0xc2, 0x4c, 0x94, 0xc2, 0x54, 0xa2, 0xc8, 0x5a, 0xab, 0xcb, 0x4e, 0x97, 0xc4, 0x49, 0x8f, 0xc0, 0x47, 0x8c, 0xbf, 0x48, 0x8e, 0xc0, 0x52, 0x9e, 0xc6, 0x51, 0x9d, 0xc6, 0x5a, 0xac, 0xcc, 0x53, 0x9f, 0xc7, 0x4d, 0x96, 0xc3, 0x4b, 0x92, 0xc2, 0x7f, 0xcb, 0x5d, 0x16, 0x0, 0x0, 0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0x60, 0x65, 0x61, 0x66, 0x62, 0x44, 0xe2, 0x73, 0x4a, 0x88, 0x8b, 0x89, 0x8a, 0x70, 0xb0, 0xb3, 0xc1, 0xe4, 0x25, 0x64, 0x65, 0x80, 0x40, 0x5a, 0x4a, 0x92, 0xb, 0x22, 0xc0, 0x22, 0x2e, 0x3, 0x1, 0xd2, 0x72, 0xdc, 0x68, 0x2, 0xf2, 0x3c, 0xa8, 0x5a, 0x14, 0x78, 0x11, 0x86, 0x2a, 0x2b, 0x29, 0xa, 0xf0, 0xf3, 0x21, 0x59, 0x2b, 0x2c, 0x24, 0xc8, 0x40, 0x3d, 0x0, 0x0, 0x19, 0x8b, 0x5, 0xfc, 0x96, 0x5c, 0x15, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vslider_tick_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x4, 0x3, 0x0, 0x0, 0x0, 0x75, 0x9a, 0xa2, 0xdf, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x1e, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x81, 0xa2, 0xad, 0x8c, 0xac, 0xb8, 0x38, 0x55, 0x5f, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xff, 0xff, 0xff, 0xe1, 0x56, 0x59, 0xc8, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x79, 0x79, 0x31, 0x1c, 0x18, 0xed, 0xfe, 0x2b, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x9, 0xf1, 0xd9, 0xa5, 0xec, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x1d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x80, 0x1, 0xd7, 0x44, 0x5, 0x6, 0x6, 0xe6, 0x92, 0x30, 0x86, 0xf2, 0x62, 0x3, 0x20, 0xa3, 0xbd, 0x1c, 0x2e, 0x3, 0x0, 0x3f, 0xce, 0x3, 0xaf, 0xf9, 0x94, 0x50, 0x96, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x4, 0x3, 0x0, 0x0, 0x0, 0x75, 0x9a, 0xa2, 0xdf, 0x0, 0x0, 0x0, 0x1b, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x81, 0xa2, 0xad, 0x8c, 0xac, 0xb8, 0x38, 0x55, 0x5f, 0x82, 0x82, 0x82, 0x8d, 0x8d, 0x8d, 0x98, 0x98, 0x98, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x7c, 0xda, 0x48, 0x6d, 0x0, 0x0, 0x0, 0x9, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x79, 0x79, 0x79, 0x31, 0x1c, 0x18, 0xed, 0xfe, 0x2b, 0x0, 0x0, 0x0, 0x1c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x80, 0x3, 0xd7, 0x44, 0x5, 0x6, 0x6, 0xe6, 0x92, 0x30, 0x86, 0xf2, 0x62, 0x3, 0x20, 0xa3, 0xbd, 0x1c, 0x2e, 0x3, 0x0, 0x3f, 0xce, 0x3, 0xaf, 0xed, 0xed, 0x7c, 0x2f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vsplit_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x1, 0xff, 0x2, 0x2d, 0xde, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0xb, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x40, 0x5, 0x0, 0x0, 0x10, 0x0, 0x1, 0xa1, 0xc5, 0x21, 0xc1, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x27, 0x27, 0x29, 0xff, 0xff, 0xff, 0x11, 0xab, 0xb9, 0xf3, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char vsplitter_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x6c, 0x9, 0xa6, 0x3, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x63, 0x60, 0xa0, 0x10, 0x30, 0x33, 0x2c, 0x10, 0x3d, 0x47, 0x9, 0x66, 0x66, 0x10, 0xbd, 0xf6, 0x98, 0x22, 0x3c, 0xe0, 0x60, 0x18, 0x84, 0x1, 0x0, 0x59, 0x34, 0x6a, 0x2d, 0x64, 0xeb, 0x72, 0x24, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x6c, 0x9, 0xa6, 0x3, 0x0, 0x0, 0x0, 0x2, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x0, 0x76, 0x93, 0xcd, 0x38, 0x0, 0x0, 0x0, 0x18, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x1c, 0x2c, 0x48, 0xa5, 0xc, 0x33, 0xa4, 0x5a, 0x53, 0x86, 0x29, 0x7, 0xa3, 0x61, 0x0, 0x0, 0x18, 0x61, 0x34, 0xa1, 0xba, 0xa4, 0x4d, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; static const unsigned char window_resizer_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x8, 0x19, 0x11, 0x33, 0x13, 0xaa, 0xc0, 0xf, 0x5f, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x18, 0x5, 0x24, 0x81, 0x17, 0x2f, 0x5e, 0xf4, 0xa3, 0x8b, 0x31, 0x91, 0xa2, 0xb9, 0xb9, 0xb9, 0x99, 0x7c, 0x9b, 0xb3, 0xb3, 0xb3, 0xfb, 0x87, 0x81, 0x66, 0x6c, 0x81, 0x48, 0x92, 0x66, 0xa2, 0x5c, 0x43, 0x91, 0xe6, 0x11, 0xa, 0x0, 0x73, 0x5b, 0x34, 0x19, 0x10, 0xa0, 0xb6, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x1e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x18, 0xbc, 0xe0, 0x45, 0x3f, 0x1, 0xe9, 0xec, 0xfe, 0x81, 0x94, 0x86, 0xb1, 0x70, 0x48, 0x23, 0x58, 0x84, 0xa4, 0x7, 0x15, 0x0, 0x0, 0xed, 0x9f, 0x18, 0xe8, 0xcd, 0x91, 0xd8, 0xe, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; // shaders block diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index e5d463d391..2f2abd4e08 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -211,9 +211,9 @@ Error DynamicFontAtSize::_load() { scale_color_font = float(id.size) / face->available_sizes[i].width; } } - error = FT_Select_Size(face, best_match); + FT_Select_Size(face, best_match); } else { - error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); } ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font; @@ -314,7 +314,7 @@ void DynamicFontAtSize::set_texture_flags(uint32_t p_flags) { texture_flags = p_flags; for (int i = 0; i < textures.size(); i++) { - Ref<ImageTexture> &tex = textures[i].texture; + Ref<ImageTexture> &tex = textures.write[i].texture; if (!tex.is_null()) tex->set_flags(p_flags); } @@ -400,7 +400,7 @@ DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyp for (int i = 0; i < textures.size(); i++) { - CharTexture &ct = textures[i]; + const CharTexture &ct = textures[i]; if (ct.texture->get_format() != p_image_format) continue; @@ -466,7 +466,7 @@ DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyp } tex.offsets.resize(texsize); for (int i = 0; i < texsize; i++) //zero offsets - tex.offsets[i] = 0; + tex.offsets.write[i] = 0; textures.push_back(tex); ret.index = textures.size() - 1; @@ -493,7 +493,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap b //fit character in char texture - CharTexture &tex = textures[tex_pos.index]; + CharTexture &tex = textures.write[tex_pos.index]; { PoolVector<uint8_t>::Write wr = tex.imgdata.write(); @@ -547,7 +547,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap b // update height array for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - tex.offsets[k] = tex_pos.y + mh; + tex.offsets.write[k] = tex_pos.y + mh; } Character chr; @@ -698,9 +698,9 @@ void DynamicFont::_reload_cache() { } for (int i = 0; i < fallbacks.size(); i++) { - fallback_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(cache_id); + fallback_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(cache_id); if (outline_cache_id.outline_size > 0) - fallback_outline_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(outline_cache_id); + fallback_outline_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(outline_cache_id); } emit_changed(); @@ -895,17 +895,17 @@ void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); ERR_FAIL_INDEX(p_idx, fallbacks.size()); - fallbacks[p_idx] = p_data; - fallback_data_at_size[p_idx] = fallbacks[p_idx]->_get_dynamic_font_at_size(cache_id); + fallbacks.write[p_idx] = p_data; + fallback_data_at_size.write[p_idx] = fallbacks.write[p_idx]->_get_dynamic_font_at_size(cache_id); } void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); fallbacks.push_back(p_data); - fallback_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. + fallback_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. if (outline_cache_id.outline_size > 0) - fallback_outline_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); + fallback_outline_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); _change_notify(); emit_changed(); @@ -1092,7 +1092,7 @@ void DynamicFont::update_oversampling() { dynamic_font_mutex->unlock(); for (int i = 0; i < changed.size(); i++) { - changed[i]->emit_changed(); + changed.write[i]->emit_changed(); } } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 0bae9d9b2d..3dfde01320 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -386,7 +386,7 @@ Vector<CharType> BitmapFont::get_char_keys() const { int count = 0; while ((ct = char_map.next(ct))) { - chars[count++] = *ct; + chars.write[count++] = *ct; }; return chars; @@ -438,7 +438,7 @@ Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const { int i = 0; for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) { - ret[i++] = E->key(); + ret.write[i++] = E->key(); } return ret; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 56b3ee70bb..875b72159a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -554,9 +554,20 @@ void SpatialMaterial::_update_shader() { case BILLBOARD_ENABLED: { code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n"; + + if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz),0,0,0),vec4(0,length(WORLD_MATRIX[1].xyz),0,0),vec4(0,0,length(WORLD_MATRIX[2].xyz),0),vec4(0,0,0,1));\n"; + } } break; case BILLBOARD_FIXED_Y: { + code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)),0.0),WORLD_MATRIX[3]);\n"; + + if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz),0,0,0),vec4(0,1,0,0),vec4(0,0,length(WORLD_MATRIX[2].xyz),0),vec4(0,0,0,1));\n"; + } else { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1,0,0,0),vec4(0,1.0/length(WORLD_MATRIX[1].xyz),0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; + } } break; case BILLBOARD_PARTICLES: { @@ -573,8 +584,6 @@ void SpatialMaterial::_update_shader() { code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n"; code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n"; code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n"; - //handle rotation - // code += "\tmat4 rotation = mat4(" } break; } @@ -1883,7 +1892,8 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "params_depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never,Opaque Pre-Pass"), "set_depth_draw_mode", "get_depth_draw_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_line_width", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_line_width", "get_line_width"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "params_billboard_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particle Billboard"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "params_billboard_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particle Billboard1"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_billboard_keep_scale"), "set_flag", "get_flag", FLAG_BILLBOARD_KEEP_SCALE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "params_grow"), "set_grow_enabled", "is_grow_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_grow_amount", PROPERTY_HINT_RANGE, "-16,10,0.01"), "set_grow", "get_grow"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_use_alpha_scissor"), "set_flag", "get_flag", FLAG_USE_ALPHA_SCISSOR); @@ -2053,6 +2063,7 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_SRGB_VERTEX_COLOR); BIND_ENUM_CONSTANT(FLAG_USE_POINT_SIZE); BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); + BIND_ENUM_CONSTANT(FLAG_BILLBOARD_KEEP_SCALE); BIND_ENUM_CONSTANT(FLAG_UV1_USE_TRIPLANAR); BIND_ENUM_CONSTANT(FLAG_UV2_USE_TRIPLANAR); BIND_ENUM_CONSTANT(FLAG_AO_ON_UV2); @@ -2095,10 +2106,10 @@ void SpatialMaterial::_bind_methods() { SpatialMaterial::SpatialMaterial() : element(this) { - //initialize to right values + // Initialize to the same values as the shader set_albedo(Color(1.0, 1.0, 1.0, 1.0)); set_specular(0.5); - set_roughness(0.0); + set_roughness(1.0); set_metallic(0.0); set_emission(Color(0, 0, 0)); set_emission_energy(1.0); diff --git a/scene/resources/material.h b/scene/resources/material.h index 84f1005b03..7a1a4acfbf 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -183,6 +183,7 @@ public: FLAG_SRGB_VERTEX_COLOR, FLAG_USE_POINT_SIZE, FLAG_FIXED_SIZE, + FLAG_BILLBOARD_KEEP_SCALE, FLAG_UV1_USE_TRIPLANAR, FLAG_UV2_USE_TRIPLANAR, FLAG_TRIPLANAR_USE_WORLD, @@ -241,7 +242,7 @@ private: uint64_t blend_mode : 2; uint64_t depth_draw_mode : 2; uint64_t cull_mode : 2; - uint64_t flags : 17; + uint64_t flags : 18; uint64_t detail_blend_mode : 2; uint64_t diffuse_mode : 3; uint64_t specular_mode : 2; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index b0620d3363..4e6004709e 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -40,7 +40,6 @@ void Mesh::_clear_triangle_mesh() const { triangle_mesh.unref(); - ; } Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { @@ -110,6 +109,54 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { return triangle_mesh; } +void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { + + Ref<TriangleMesh> tm = generate_triangle_mesh(); + if (tm.is_null()) + return; + + PoolVector<int> triangle_indices; + tm->get_indices(&triangle_indices); + const int triangles_num = tm->get_triangles().size(); + PoolVector<Vector3> vertices = tm->get_vertices(); + + r_lines.resize(tm->get_triangles().size() * 6); // 3 lines x 2 points each line + + PoolVector<int>::Read ind_r = triangle_indices.read(); + PoolVector<Vector3>::Read ver_r = vertices.read(); + for (int j = 0, x = 0, i = 0; i < triangles_num; j += 6, x += 3, ++i) { + // Triangle line 1 + r_lines.write[j + 0] = ver_r[ind_r[x + 0]]; + r_lines.write[j + 1] = ver_r[ind_r[x + 1]]; + + // Triangle line 2 + r_lines.write[j + 2] = ver_r[ind_r[x + 1]]; + r_lines.write[j + 3] = ver_r[ind_r[x + 2]]; + + // Triangle line 3 + r_lines.write[j + 4] = ver_r[ind_r[x + 2]]; + r_lines.write[j + 5] = ver_r[ind_r[x + 0]]; + } +} +void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { + Ref<TriangleMesh> tm = generate_triangle_mesh(); + if (tm.is_null()) + return; + + PoolVector<Vector3> vertices = tm->get_vertices(); + + int vertices_size = vertices.size(); + r_points.resize(vertices_size); + for (int i = 0; i < vertices_size; ++i) { + r_points.write[i] = vertices[i]; + } +} + +bool Mesh::surface_is_softbody_friendly(int p_idx) const { + const uint32_t surface_format = surface_get_format(p_idx); + return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_VERTEX)) && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL))); +} + PoolVector<Face3> Mesh::get_faces() const { Ref<TriangleMesh> tm = generate_triangle_mesh(); @@ -484,6 +531,10 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(ARRAY_MAX); } +void Mesh::clear_cache() { + _clear_triangle_mesh(); +} + Mesh::Mesh() { } @@ -616,7 +667,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { bone_aabb.resize(baabb.size()); for (int i = 0; i < baabb.size(); i++) { - bone_aabb[i] = baabb[i]; + bone_aabb.write[i] = baabb[i]; } } @@ -788,8 +839,8 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array & aabb.expand_to(vtx[i]); } - surfaces[surfaces.size() - 1].aabb = aabb; - surfaces[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY; + surfaces.write[surfaces.size() - 1].aabb = aabb; + surfaces.write[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY; _recompute_aabb(); } @@ -908,18 +959,28 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) ERR_FAIL_INDEX(p_idx, surfaces.size()); if (surfaces[p_idx].material == p_material) return; - surfaces[p_idx].material = p_material; + surfaces.write[p_idx].material = p_material; VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid()); _change_notify("material"); emit_changed(); } +int ArrayMesh::surface_find_by_name(const String &p_name) const { + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].name == p_name) { + return i; + } + } + + return -1; +} + void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { ERR_FAIL_INDEX(p_idx, surfaces.size()); - surfaces[p_idx].name = p_name; + surfaces.write[p_idx].name = p_name; emit_changed(); } @@ -939,7 +1000,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { ERR_FAIL_INDEX(p_idx, surfaces.size()); - surfaces[p_idx].aabb = p_aabb; + surfaces.write[p_idx].aabb = p_aabb; // set custom aabb too? emit_changed(); } @@ -1042,8 +1103,8 @@ void ArrayMesh::regen_normalmaps() { for (int i = 0; i < surfs.size(); i++) { - surfs[i]->generate_tangents(); - surfs[i]->commit(Ref<ArrayMesh>(this)); + surfs.write[i]->generate_tangents(); + surfs.write[i]->commit(Ref<ArrayMesh>(this)); } } @@ -1108,13 +1169,13 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe Vector3 v = p_base_transform.xform(r[j]); Vector3 n = p_base_transform.basis.xform(rn[j]).normalized(); - vertices[(j + vertex_ofs) * 3 + 0] = v.x; - vertices[(j + vertex_ofs) * 3 + 1] = v.y; - vertices[(j + vertex_ofs) * 3 + 2] = v.z; - normals[(j + vertex_ofs) * 3 + 0] = n.x; - normals[(j + vertex_ofs) * 3 + 1] = n.y; - normals[(j + vertex_ofs) * 3 + 2] = n.z; - uv_index[j + vertex_ofs] = Pair<int, int>(i, j); + vertices.write[(j + vertex_ofs) * 3 + 0] = v.x; + vertices.write[(j + vertex_ofs) * 3 + 1] = v.y; + vertices.write[(j + vertex_ofs) * 3 + 2] = v.z; + normals.write[(j + vertex_ofs) * 3 + 0] = n.x; + normals.write[(j + vertex_ofs) * 3 + 1] = n.y; + normals.write[(j + vertex_ofs) * 3 + 2] = n.z; + uv_index.write[j + vertex_ofs] = Pair<int, int>(i, j); } PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX]; @@ -1197,31 +1258,31 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second]; if (surfaces[surface].format & ARRAY_FORMAT_COLOR) { - surfaces_tools[surface]->add_color(v.color); + surfaces_tools.write[surface]->add_color(v.color); } if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { - surfaces_tools[surface]->add_uv(v.uv); + surfaces_tools.write[surface]->add_uv(v.uv); } if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) { - surfaces_tools[surface]->add_normal(v.normal); + surfaces_tools.write[surface]->add_normal(v.normal); } if (surfaces[surface].format & ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools[surface]->add_tangent(t); + surfaces_tools.write[surface]->add_tangent(t); } if (surfaces[surface].format & ARRAY_FORMAT_BONES) { - surfaces_tools[surface]->add_bones(v.bones); + surfaces_tools.write[surface]->add_bones(v.bones); } if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { - surfaces_tools[surface]->add_weights(v.weights); + surfaces_tools.write[surface]->add_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools[surface]->add_uv2(uv2); + surfaces_tools.write[surface]->add_uv2(uv2); - surfaces_tools[surface]->add_vertex(v.vertex); + surfaces_tools.write[surface]->add_vertex(v.vertex); } } @@ -1233,8 +1294,8 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe //generate surfaces for (int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools[i]->index(); - surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format); + surfaces_tools.write[i]->index(); + surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); @@ -1261,6 +1322,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_get_primitive_type", "surf_idx"), &ArrayMesh::surface_get_primitive_type); ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &ArrayMesh::surface_set_material); ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &ArrayMesh::surface_get_material); + ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name); ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &ArrayMesh::surface_get_arrays); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index a3fb068569..36bfca49f8 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -123,6 +123,7 @@ public: virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; + virtual bool surface_is_softbody_friendly(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const = 0; virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual uint32_t surface_get_format(int p_idx) const = 0; @@ -133,6 +134,8 @@ public: PoolVector<Face3> get_faces() const; Ref<TriangleMesh> generate_triangle_mesh() const; + void generate_debug_mesh_lines(Vector<Vector3> &r_lines); + void generate_debug_mesh_indices(Vector<Vector3> &r_points); Ref<Shape> create_trimesh_shape() const; Ref<Shape> create_convex_shape() const; @@ -143,6 +146,7 @@ public: void set_lightmap_size_hint(const Vector2 &p_size); Size2 get_lightmap_size_hint() const; + void clear_cache(); Mesh(); }; @@ -208,6 +212,7 @@ public: void surface_set_material(int p_idx, const Ref<Material> &p_material); Ref<Material> surface_get_material(int p_idx) const; + int surface_find_by_name(const String &p_name) const; void surface_set_name(int p_idx, const String &p_name); String surface_get_name(int p_idx) const; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 8639b325c3..6732303925 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -120,7 +120,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf v.bones.push_back(bo[i * 4 + 3]); } - vertices[i] = v; + vertices.write[i] = v; } PoolVector<int> indices; @@ -143,7 +143,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf for (int i = 0; i < icount; i += 3) { - Vertex *v[3] = { &vertices[r[i + 0]], &vertices[r[i + 1]], &vertices[r[i + 2]] }; + Vertex *v[3] = { &vertices.write[r[i + 0]], &vertices.write[r[i + 1]], &vertices.write[r[i + 2]] }; int fidx = faces.size(); Face face; @@ -169,7 +169,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf edges.push_back(e); } - edges[face.edges[j]].faces.push_back(fidx); + edges.write[face.edges[j]].faces.push_back(fidx); v[j]->faces.push_back(fidx); v[j]->edges.push_back(face.edges[j]); } @@ -247,7 +247,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { for (int i = 0; i < vcount; i++) { - Vertex &vtx = vertices[i]; + const Vertex &vtx = vertices[i]; vr[i] = vtx.vertex; if (nr.ptr()) @@ -344,7 +344,7 @@ Vector3 MeshDataTool::get_vertex(int p_idx) const { void MeshDataTool::set_vertex(int p_idx, const Vector3 &p_vertex) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].vertex = p_vertex; + vertices.write[p_idx].vertex = p_vertex; } Vector3 MeshDataTool::get_vertex_normal(int p_idx) const { @@ -355,7 +355,7 @@ Vector3 MeshDataTool::get_vertex_normal(int p_idx) const { void MeshDataTool::set_vertex_normal(int p_idx, const Vector3 &p_normal) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].normal = p_normal; + vertices.write[p_idx].normal = p_normal; format |= Mesh::ARRAY_FORMAT_NORMAL; } @@ -367,7 +367,7 @@ Plane MeshDataTool::get_vertex_tangent(int p_idx) const { void MeshDataTool::set_vertex_tangent(int p_idx, const Plane &p_tangent) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].tangent = p_tangent; + vertices.write[p_idx].tangent = p_tangent; format |= Mesh::ARRAY_FORMAT_TANGENT; } @@ -379,7 +379,7 @@ Vector2 MeshDataTool::get_vertex_uv(int p_idx) const { void MeshDataTool::set_vertex_uv(int p_idx, const Vector2 &p_uv) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].uv = p_uv; + vertices.write[p_idx].uv = p_uv; format |= Mesh::ARRAY_FORMAT_TEX_UV; } @@ -391,7 +391,7 @@ Vector2 MeshDataTool::get_vertex_uv2(int p_idx) const { void MeshDataTool::set_vertex_uv2(int p_idx, const Vector2 &p_uv2) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].uv2 = p_uv2; + vertices.write[p_idx].uv2 = p_uv2; format |= Mesh::ARRAY_FORMAT_TEX_UV2; } @@ -403,7 +403,7 @@ Color MeshDataTool::get_vertex_color(int p_idx) const { void MeshDataTool::set_vertex_color(int p_idx, const Color &p_color) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].color = p_color; + vertices.write[p_idx].color = p_color; format |= Mesh::ARRAY_FORMAT_COLOR; } @@ -415,7 +415,7 @@ Vector<int> MeshDataTool::get_vertex_bones(int p_idx) const { void MeshDataTool::set_vertex_bones(int p_idx, const Vector<int> &p_bones) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].bones = p_bones; + vertices.write[p_idx].bones = p_bones; format |= Mesh::ARRAY_FORMAT_BONES; } @@ -426,7 +426,7 @@ Vector<float> MeshDataTool::get_vertex_weights(int p_idx) const { } void MeshDataTool::set_vertex_weights(int p_idx, const Vector<float> &p_weights) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].weights = p_weights; + vertices.write[p_idx].weights = p_weights; format |= Mesh::ARRAY_FORMAT_WEIGHTS; } @@ -439,7 +439,7 @@ Variant MeshDataTool::get_vertex_meta(int p_idx) const { void MeshDataTool::set_vertex_meta(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].meta = p_meta; + vertices.write[p_idx].meta = p_meta; } Vector<int> MeshDataTool::get_vertex_edges(int p_idx) const { @@ -472,7 +472,7 @@ Variant MeshDataTool::get_edge_meta(int p_idx) const { void MeshDataTool::set_edge_meta(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, edges.size()); - edges[p_idx].meta = p_meta; + edges.write[p_idx].meta = p_meta; } int MeshDataTool::get_face_vertex(int p_face, int p_vertex) const { @@ -495,7 +495,7 @@ Variant MeshDataTool::get_face_meta(int p_face) const { void MeshDataTool::set_face_meta(int p_face, const Variant &p_meta) { ERR_FAIL_INDEX(p_face, faces.size()); - faces[p_face].meta = p_meta; + faces.write[p_face].meta = p_meta; } Vector3 MeshDataTool::get_face_normal(int p_face) const { diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index e1d3540fd1..a1d3e0ba1e 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -209,7 +209,7 @@ Vector<int> MeshLibrary::get_item_list() const { int idx = 0; for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - ret[idx++] = E->key(); + ret.write[idx++] = E->key(); } return ret; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 846f6e356e..07783d5f4a 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -31,6 +31,7 @@ #include "packed_scene.h" #include "core/core_string_names.h" +#include "engine.h" #include "io/resource_loader.h" #include "project_settings.h" #include "scene/2d/node_2d.h" @@ -279,7 +280,12 @@ Node *SceneState::instance(GenEditState p_edit_state) const { stray_instances.push_back(node); //can't be added, go to stray list } } else { - node->_set_name_nocheck(snames[n.name]); + if (Engine::get_singleton()->is_editor_hint()) { + //validate name if using editor, to avoid broken + node->set_name(snames[n.name]); + } else { + node->_set_name_nocheck(snames[n.name]); + } } } @@ -325,7 +331,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (c.binds.size()) { binds.resize(c.binds.size()); for (int j = 0; j < c.binds.size(); j++) - binds[j] = props[c.binds[j]]; + binds.write[j] = props[c.binds[j]]; } cfrom->connect(snames[c.signal], cto, snames[c.method], binds, CONNECT_PERSIST | c.flags); @@ -389,7 +395,15 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.name = _nm_get_string(p_node->get_name(), name_map); nd.instance = -1; //not instanced by default - nd.index = p_node->get_index(); + + //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene + if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { + //do not save index, because it belongs to saved scene and scene is not inherited + nd.index = -1; + } else { + //part of an inherited scene, or parent is from an instanced scene + nd.index = p_node->get_index(); + } // if this node is part of an instanced scene or sub-instanced scene // we need to get the corresponding instance states. @@ -875,7 +889,7 @@ Error SceneState::pack(Node *p_scene) { for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) { - names[E->get()] = E->key(); + names.write[E->get()] = E->key(); } variants.resize(variant_map.size()); @@ -883,13 +897,13 @@ Error SceneState::pack(Node *p_scene) { while ((K = variant_map.next(K))) { int idx = variant_map[*K]; - variants[idx] = *K; + variants.write[idx] = *K; } node_paths.resize(nodepath_map.size()); for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) { - node_paths[E->get()] = scene->get_path_to(E->key()); + node_paths.write[E->get()] = scene->get_path_to(E->key()); } return OK; @@ -1090,7 +1104,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { names.resize(namecount); PoolVector<String>::Read r = snames.read(); for (int i = 0; i < names.size(); i++) - names[i] = r[i]; + names.write[i] = r[i]; } Array svariants = p_dictionary["variants"]; @@ -1100,7 +1114,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { variants.resize(varcount); for (int i = 0; i < varcount; i++) { - variants[i] = svariants[i]; + variants.write[i] = svariants[i]; } } else { @@ -1114,7 +1128,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { PoolVector<int>::Read r = snodes.read(); int idx = 0; for (int i = 0; i < nc; i++) { - NodeData &nd = nodes[i]; + NodeData &nd = nodes.write[i]; nd.parent = r[idx++]; nd.owner = r[idx++]; nd.type = r[idx++]; @@ -1126,13 +1140,13 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { nd.properties.resize(r[idx++]); for (int j = 0; j < nd.properties.size(); j++) { - nd.properties[j].name = r[idx++]; - nd.properties[j].value = r[idx++]; + nd.properties.write[j].name = r[idx++]; + nd.properties.write[j].value = r[idx++]; } nd.groups.resize(r[idx++]); for (int j = 0; j < nd.groups.size(); j++) { - nd.groups[j] = r[idx++]; + nd.groups.write[j] = r[idx++]; } } } @@ -1146,7 +1160,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { PoolVector<int>::Read r = sconns.read(); int idx = 0; for (int i = 0; i < cc; i++) { - ConnectionData &cd = connections[i]; + ConnectionData &cd = connections.write[i]; cd.from = r[idx++]; cd.to = r[idx++]; cd.signal = r[idx++]; @@ -1156,7 +1170,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { for (int j = 0; j < cd.binds.size(); j++) { - cd.binds[j] = r[idx++]; + cd.binds.write[j] = r[idx++]; } } } @@ -1167,7 +1181,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { } node_paths.resize(np.size()); for (int i = 0; i < np.size(); i++) { - node_paths[i] = np[i]; + node_paths.write[i] = np[i]; } Array ei; @@ -1181,7 +1195,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { editable_instances.resize(ei.size()); for (int i = 0; i < editable_instances.size(); i++) { - editable_instances[i] = ei[i]; + editable_instances.write[i] = ei[i]; } //path=p_dictionary["path"]; @@ -1563,13 +1577,13 @@ void SceneState::add_node_property(int p_node, int p_name, int p_value) { NodeData::Property prop; prop.name = p_name; prop.value = p_value; - nodes[p_node].properties.push_back(prop); + nodes.write[p_node].properties.push_back(prop); } void SceneState::add_node_group(int p_node, int p_group) { ERR_FAIL_INDEX(p_node, nodes.size()); ERR_FAIL_INDEX(p_group, names.size()); - nodes[p_node].groups.push_back(p_group); + nodes.write[p_node].groups.push_back(p_group); } void SceneState::set_base_scene(int p_idx) { diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp new file mode 100644 index 0000000000..dc5ca1aef6 --- /dev/null +++ b/scene/resources/physics_material.cpp @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* physics_material.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "physics_material.h" + +void PhysicsMaterial::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicsMaterial::set_friction); + ClassDB::bind_method(D_METHOD("get_friction"), &PhysicsMaterial::get_friction); + + ClassDB::bind_method(D_METHOD("set_rough", "rough"), &PhysicsMaterial::set_rough); + ClassDB::bind_method(D_METHOD("is_rough"), &PhysicsMaterial::is_rough); + + ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicsMaterial::set_bounce); + ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicsMaterial::get_bounce); + + ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent); + ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction"), "set_friction", "get_friction"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent"); +} + +void PhysicsMaterial::set_friction(real_t p_val) { + friction = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_rough(bool p_val) { + rough = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_bounce(real_t p_val) { + bounce = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_absorbent(bool p_val) { + absorbent = p_val; + emit_changed(); +} + +PhysicsMaterial::PhysicsMaterial() : + friction(1), + rough(false), + bounce(0), + absorbent(false) {} diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h new file mode 100644 index 0000000000..dfe48d94cf --- /dev/null +++ b/scene/resources/physics_material.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* physics_material.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef physics_material_override_H +#define physics_material_override_H + +#include "resource.h" +#include "servers/physics_server.h" + +class PhysicsMaterial : public Resource { + + GDCLASS(PhysicsMaterial, Resource); + OBJ_SAVE_TYPE(PhysicsMaterial); + RES_BASE_EXTENSION("PhyMat"); + + real_t friction; + bool rough; + real_t bounce; + bool absorbent; + +protected: + static void _bind_methods(); + +public: + void set_friction(real_t p_val); + _FORCE_INLINE_ real_t get_friction() const { return friction; } + + void set_rough(bool p_val); + _FORCE_INLINE_ bool is_rough() const { return rough; } + + _FORCE_INLINE_ real_t computed_friction() const { + return rough ? -friction : friction; + } + + void set_bounce(real_t p_val); + _FORCE_INLINE_ real_t get_bounce() const { return bounce; } + + void set_absorbent(bool p_val); + _FORCE_INLINE_ bool is_absorbent() const { return absorbent; } + + _FORCE_INLINE_ real_t computed_bounce() const { + return absorbent ? -bounce : bounce; + } + + PhysicsMaterial(); +}; + +#endif // physics_material_override_H diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 6fea2e1a8e..44f9ebaf33 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -65,8 +65,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> for (int i = 0; i < p_points.size(); i++) { - points[i].pos = p_points[i]; - points[i].penalty = 0; + points.write[i].pos = p_points[i]; + points.write[i].penalty = 0; outside_point.x = i == 0 ? p_points[0].x : (MAX(p_points[i].x, outside_point.x)); outside_point.y = i == 0 ? p_points[0].y : (MAX(p_points[i].y, outside_point.y)); @@ -88,8 +88,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> Edge e(p_connections[i], p_connections[i + 1]); ERR_FAIL_INDEX(e.points[0], point_count); ERR_FAIL_INDEX(e.points[1], point_count); - points[p_connections[i]].connections.insert(p_connections[i + 1]); - points[p_connections[i + 1]].connections.insert(p_connections[i]); + points.write[p_connections[i]].connections.insert(p_connections[i + 1]); + points.write[p_connections[i + 1]].connections.insert(p_connections[i]); edges.insert(e); } @@ -126,8 +126,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> } if (valid) { - points[i].connections.insert(j); - points[j].connections.insert(i); + points.write[i].connections.insert(j); + points.write[j].connections.insert(i); } } } @@ -227,21 +227,21 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector int aidx = points.size() - 2; int bidx = points.size() - 1; - points[aidx].pos = from; - points[bidx].pos = to; - points[aidx].distance = 0; - points[bidx].distance = 0; - points[aidx].prev = -1; - points[bidx].prev = -1; - points[aidx].penalty = 0; - points[bidx].penalty = 0; + points.write[aidx].pos = from; + points.write[bidx].pos = to; + points.write[aidx].distance = 0; + points.write[bidx].distance = 0; + points.write[aidx].prev = -1; + points.write[bidx].prev = -1; + points.write[aidx].penalty = 0; + points.write[bidx].penalty = 0; for (int i = 0; i < points.size() - 2; i++) { bool valid_a = true; bool valid_b = true; - points[i].prev = -1; - points[i].distance = 0; + points.write[i].prev = -1; + points.write[i].distance = 0; if (!_is_point_inside(from * 0.5 + points[i].pos * 0.5)) { valid_a = false; @@ -292,26 +292,26 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } if (valid_a) { - points[i].connections.insert(aidx); - points[aidx].connections.insert(i); + points.write[i].connections.insert(aidx); + points.write[aidx].connections.insert(i); } if (valid_b) { - points[i].connections.insert(bidx); - points[bidx].connections.insert(i); + points.write[i].connections.insert(bidx); + points.write[bidx].connections.insert(i); } } //solve graph Set<int> open_list; - points[aidx].distance = 0; - points[aidx].prev = aidx; + points.write[aidx].distance = 0; + points.write[aidx].prev = aidx; for (Set<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) { open_list.insert(E->get()); - points[E->get()].distance = from.distance_to(points[E->get()].pos); - points[E->get()].prev = aidx; + points.write[E->get()].distance = from.distance_to(points[E->get()].pos); + points.write[E->get()].prev = aidx; } bool found_route = false; @@ -342,12 +342,12 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } } - Point &np = points[least_cost_point]; + const Point &np = points[least_cost_point]; //open the neighbours for search for (Set<int>::Element *E = np.connections.front(); E; E = E->next()) { - Point &p = points[E->get()]; + Point &p = points.write[E->get()]; float distance = np.pos.distance_to(p.pos) + np.distance; if (p.prev != -1) { @@ -392,18 +392,18 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector for (int i = 0; i < points.size() - 2; i++) { - points[i].connections.erase(aidx); - points[i].connections.erase(bidx); - points[i].prev = -1; - points[i].distance = 0; + points.write[i].connections.erase(aidx); + points.write[i].connections.erase(bidx); + points.write[i].prev = -1; + points.write[i].distance = 0; } - points[aidx].connections.clear(); - points[aidx].prev = -1; - points[aidx].distance = 0; - points[bidx].connections.clear(); - points[bidx].prev = -1; - points[bidx].distance = 0; + points.write[aidx].connections.clear(); + points.write[aidx].prev = -1; + points.write[aidx].distance = 0; + points.write[bidx].connections.clear(); + points.write[bidx].prev = -1; + points.write[bidx].distance = 0; return path; } @@ -427,13 +427,13 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) { PoolVector<Vector2>::Read pr = p.read(); for (int i = 0; i < pc; i++) { - points[i].pos = pr[i]; + points.write[i].pos = pr[i]; PoolVector<int> con = c[i]; PoolVector<int>::Read cr = con.read(); int cc = con.size(); for (int j = 0; j < cc; j++) { - points[i].connections.insert(cr[j]); + points.write[i].connections.insert(cr[j]); } } @@ -443,7 +443,7 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) { if (penalties.size() == pc) { PoolVector<float>::Read pr = penalties.read(); for (int i = 0; i < pc; i++) { - points[i].penalty = pr[i]; + points.write[i].penalty = pr[i]; } } } @@ -566,7 +566,7 @@ Rect2 PolygonPathFinder::get_bounds() const { void PolygonPathFinder::set_point_penalty(int p_point, float p_penalty) { ERR_FAIL_INDEX(p_point, points.size() - 2); - points[p_point].penalty = p_penalty; + points.write[p_point].penalty = p_penalty; } float PolygonPathFinder::get_point_penalty(int p_point) const { diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 9df117d09c..fd9989fe72 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -1523,7 +1523,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - sorted_er[E->get()] = E->key(); + sorted_er.write[E->get()] = E->key(); } for (int i = 0; i < sorted_er.size(); i++) { diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp index f2d5cb3516..4176aed4d8 100644 --- a/scene/resources/sky_box.cpp +++ b/scene/resources/sky_box.cpp @@ -405,7 +405,7 @@ void ProceduralSky::_update_sky() { } else { Ref<Image> image = _generate_sky(); - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), Image::FORMAT_RGBE9995, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); VS::get_singleton()->texture_set_data(texture, image); _radiance_changed(); } @@ -422,7 +422,7 @@ void ProceduralSky::_queue_update() { void ProceduralSky::_thread_done(const Ref<Image> &p_image) { - VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), Image::FORMAT_RGBE9995, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); VS::get_singleton()->texture_set_data(texture, p_image); _radiance_changed(); Thread::wait_to_finish(sky_thread); @@ -532,14 +532,14 @@ ProceduralSky::ProceduralSky() { texture = VS::get_singleton()->texture_create(); update_queued = false; - sky_top_color = Color::hex(0x0c74f9ff); - sky_horizon_color = Color::hex(0x8ed2e8ff); - sky_curve = 0.25; + sky_top_color = Color::hex(0xa5d6f1ff); + sky_horizon_color = Color::hex(0xd6eafaff); + sky_curve = 0.09; sky_energy = 1; - ground_bottom_color = Color::hex(0x1a2530ff); - ground_horizon_color = Color::hex(0x7bc9f3ff); - ground_curve = 0.01; + ground_bottom_color = Color::hex(0x282f36ff); + ground_horizon_color = Color::hex(0x6c655fff); + ground_curve = 0.02; ground_energy = 1; sun_color = Color(1, 1, 1); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 5a42873d79..ec489e5c5b 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -421,6 +421,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_ Array a = commit_to_arrays(); mesh->add_surface_from_arrays(primitive, a, Array(), p_flags); + if (material.is_valid()) mesh->surface_set_material(surface, material); @@ -465,7 +466,7 @@ void SurfaceTool::deindex() { int idx = 0; for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - varr[idx++] = E->get(); + varr.write[idx++] = E->get(); } vertex_array.clear(); for (List<int>::Element *E = index_array.front(); E; E = E->next()) { @@ -569,19 +570,19 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array if (lformat & VS::ARRAY_FORMAT_BONES) { Vector<int> b; b.resize(4); - b[0] = barr[i * 4 + 0]; - b[1] = barr[i * 4 + 1]; - b[2] = barr[i * 4 + 2]; - b[3] = barr[i * 4 + 3]; + b.write[0] = barr[i * 4 + 0]; + b.write[1] = barr[i * 4 + 1]; + b.write[2] = barr[i * 4 + 2]; + b.write[3] = barr[i * 4 + 3]; v.bones = b; } if (lformat & VS::ARRAY_FORMAT_WEIGHTS) { Vector<float> w; w.resize(4); - w[0] = warr[i * 4 + 0]; - w[1] = warr[i * 4 + 1]; - w[2] = warr[i * 4 + 2]; - w[3] = warr[i * 4 + 3]; + w.write[0] = warr[i * 4 + 0]; + w.write[1] = warr[i * 4 + 1]; + w.write[2] = warr[i * 4 + 2]; + w.write[3] = warr[i * 4 + 3]; v.weights = w; } @@ -674,19 +675,19 @@ void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, Li if (lformat & VS::ARRAY_FORMAT_BONES) { Vector<int> b; b.resize(4); - b[0] = barr[i * 4 + 0]; - b[1] = barr[i * 4 + 1]; - b[2] = barr[i * 4 + 2]; - b[3] = barr[i * 4 + 3]; + b.write[0] = barr[i * 4 + 0]; + b.write[1] = barr[i * 4 + 1]; + b.write[2] = barr[i * 4 + 2]; + b.write[3] = barr[i * 4 + 3]; v.bones = b; } if (lformat & VS::ARRAY_FORMAT_WEIGHTS) { Vector<float> w; w.resize(4); - w[0] = warr[i * 4 + 0]; - w[1] = warr[i * 4 + 1]; - w[2] = warr[i * 4 + 2]; - w[3] = warr[i * 4 + 3]; + w.write[0] = warr[i * 4 + 0]; + w.write[1] = warr[i * 4 + 1]; + w.write[2] = warr[i * 4 + 2]; + w.write[3] = warr[i * 4 + 3]; v.weights = w; } @@ -845,7 +846,7 @@ void SurfaceTool::generate_tangents() { vtx.resize(vertex_array.size()); int idx = 0; for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - vtx[idx++] = E; + vtx.write[idx++] = E; E->get().binormal = Vector3(); E->get().tangent = Vector3(); } diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp new file mode 100644 index 0000000000..e2fe0adfc5 --- /dev/null +++ b/scene/resources/text_file.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* text_file.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "text_file.h" + +#include "os/file_access.h" + +bool TextFile::has_text() const { + return text != ""; +} + +String TextFile::get_text() const { + return text; +} + +void TextFile::set_text(const String &p_code) { + text = p_code; +} + +void TextFile::reload_from_file() { + load_text(path); +} + +Error TextFile::load_text(const String &p_path) { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + if (err) { + ERR_FAIL_COND_V(err, err); + } + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + ERR_FAIL_V(ERR_INVALID_DATA); + } + text = s; + path = p_path; + return OK; +} diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h new file mode 100644 index 0000000000..40b648eebb --- /dev/null +++ b/scene/resources/text_file.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* text_file.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXTFILE_H +#define TEXTFILE_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" + +class TextFile : public Resource { + + GDCLASS(TextFile, Resource) + +private: + String text; + String path; + +public: + virtual bool has_text() const; + virtual String get_text() const; + virtual void set_text(const String &p_code); + virtual void reload_from_file(); + + void set_file_path(const String &p_path) { path = p_path; } + Error load_text(const String &p_path); +}; + +#endif // TEXTFILE_H diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 2baad555c0..536c653a0c 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -124,7 +124,7 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { Size2 s = p_value; w = s.width; h = s.height; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } else if (p_name == "_data") { _set_data(p_value); } else @@ -151,13 +151,6 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat")); p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); @@ -183,7 +176,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) { void ImageTexture::create(int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { flags = p_flags; - VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, p_format, p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, 0, p_format, VS::TEXTURE_TYPE_2D, p_flags); format = p_format; w = p_width; h = p_height; @@ -196,7 +189,7 @@ void ImageTexture::create_from_image(const Ref<Image> &p_image, uint32_t p_flags h = p_image->get_height(); format = p_image->get_format(); - VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_flags); VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); } @@ -235,6 +228,8 @@ Error ImageTexture::load(const String &p_path) { void ImageTexture::set_data(const Ref<Image> &p_image) { + ERR_FAIL_COND(p_image.is_null()); + VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); @@ -299,7 +294,7 @@ void ImageTexture::set_size_override(const Size2 &p_size) { w = s.x; if (s.y != 0) h = s.y; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } void ImageTexture::set_path(const String &p_path, bool p_take_over) { @@ -590,7 +585,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla int sh = th; int mipmaps = Image::get_image_required_mipmaps(tw, th, format); - int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + int total_size = Image::get_image_data_size(tw, th, format, true); int idx = 0; int ofs = 0; @@ -646,7 +641,7 @@ Error StreamTexture::load(const String &p_path) { if (err) return err; - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), image->get_format(), lflags); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, image->get_format(), VS::TEXTURE_TYPE_2D, lflags); VS::get_singleton()->texture_set_data(texture, image); w = lw; @@ -1037,7 +1032,7 @@ bool LargeTexture::has_alpha() const { void LargeTexture::set_flags(uint32_t p_flags) { for (int i = 0; i < pieces.size(); i++) { - pieces[i].texture->set_flags(p_flags); + pieces.write[i].texture->set_flags(p_flags); } } @@ -1063,13 +1058,13 @@ int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture> &p_textur void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].offset = p_offset; + pieces.write[p_idx].offset = p_offset; }; void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture> &p_texture) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].texture = p_texture; + pieces.write[p_idx].texture = p_texture; }; void LargeTexture::set_size(const Size2 &p_size) { @@ -1153,7 +1148,6 @@ void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile Size2 scale = p_rect.size / size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1168,7 +1162,6 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons Size2 scale = p_rect.size / p_src_rect.size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1193,7 +1186,7 @@ void CubeMap::set_flags(uint32_t p_flags) { flags = p_flags; if (_is_valid()) - VS::get_singleton()->texture_set_flags(cubemap, flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_set_flags(cubemap, flags); } uint32_t CubeMap::get_flags() const { @@ -1209,7 +1202,7 @@ void CubeMap::set_side(Side p_side, const Ref<Image> &p_image) { format = p_image->get_format(); w = p_image->get_width(); h = p_image->get_height(); - VS::get_singleton()->texture_allocate(cubemap, w, h, p_image->get_format(), flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_allocate(cubemap, w, h, 0, p_image->get_format(), VS::TEXTURE_TYPE_CUBEMAP, flags); } VS::get_singleton()->texture_set_data(cubemap, p_image, VS::CubeMapSide(p_side)); @@ -1320,13 +1313,6 @@ bool CubeMap::_get(const StringName &p_name, Variant &r_ret) const { void CubeMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::OBJECT, "side/left", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/right", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/bottom", PROPERTY_HINT_RESOURCE_TYPE, "Image")); @@ -1472,7 +1458,7 @@ void CurveTexture::_update() { Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data)); - VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(_texture, _width, 1, 0, Image::FORMAT_RF, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(_texture, image); emit_changed(); @@ -1581,7 +1567,7 @@ void GradientTexture::_update() { Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); - VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RGBA8, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(texture, width, 1, 0, Image::FORMAT_RGBA8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(texture, image); emit_changed(); @@ -1874,3 +1860,327 @@ AnimatedTexture::AnimatedTexture() { AnimatedTexture::~AnimatedTexture() { VS::get_singleton()->free(proxy); } +/////////////////////////////// + +void TextureLayered::set_flags(uint32_t p_flags) { + flags = p_flags; + + if (texture.is_valid()) { + VS::get_singleton()->texture_set_flags(texture, flags); + } +} + +uint32_t TextureLayered::get_flags() const { + return flags; +} + +Image::Format TextureLayered::get_format() const { + return format; +} + +uint32_t TextureLayered::get_width() const { + return width; +} + +uint32_t TextureLayered::get_height() const { + return height; +} + +uint32_t TextureLayered::get_depth() const { + return depth; +} + +void TextureLayered::_set_data(const Dictionary &p_data) { + ERR_FAIL_COND(!p_data.has("width")); + ERR_FAIL_COND(!p_data.has("height")); + ERR_FAIL_COND(!p_data.has("depth")); + ERR_FAIL_COND(!p_data.has("format")); + ERR_FAIL_COND(!p_data.has("flags")); + ERR_FAIL_COND(!p_data.has("layers")); + int w = p_data["width"]; + int h = p_data["height"]; + int d = p_data["depth"]; + Image::Format format = Image::Format(int(p_data["format"])); + int flags = p_data["flags"]; + Array layers = p_data["layers"]; + ERR_FAIL_COND(layers.size() != d); + + create(w, h, d, format, flags); + + for (int i = 0; i < layers.size(); i++) { + Ref<Image> img = layers[i]; + ERR_CONTINUE(!img.is_valid()); + ERR_CONTINUE(img->get_format() != format); + ERR_CONTINUE(img->get_width() != w); + ERR_CONTINUE(img->get_height() != h); + set_layer_data(img, i); + } +} + +Dictionary TextureLayered::_get_data() const { + Dictionary d; + d["width"] = width; + d["height"] = height; + d["depth"] = depth; + d["flags"] = flags; + d["format"] = format; + + Array layers; + for (int i = 0; i < depth; i++) { + layers.push_back(get_layer_data(i)); + } + d["layers"] = layers; + return d; +} + +void TextureLayered::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags) { + VS::get_singleton()->texture_allocate(texture, p_width, p_height, p_depth, p_format, is_3d ? VS::TEXTURE_TYPE_3D : VS::TEXTURE_TYPE_2D_ARRAY, p_flags); + + width = p_width; + height = p_height; + depth = p_depth; + + flags = p_flags; +} + +void TextureLayered::set_layer_data(const Ref<Image> &p_image, int p_layer) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data(texture, p_image, p_layer); +} + +Ref<Image> TextureLayered::get_layer_data(int p_layer) const { + + ERR_FAIL_COND_V(!texture.is_valid(), Ref<Image>()); + return VS::get_singleton()->texture_get_data(texture, p_layer); +} + +void TextureLayered::set_data_partial(const Ref<Image> &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data_partial(texture, p_image, 0, 0, p_image->get_width(), p_image->get_height(), p_x_ofs, p_y_ofs, p_mipmap, p_z); +} + +RID TextureLayered::get_rid() const { + return texture; +} + +void TextureLayered::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + VS::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +void TextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextureLayered::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextureLayered::get_flags); + + ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format); + + ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width); + ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height); + ClassDB::bind_method(D_METHOD("get_depth"), &TextureLayered::get_depth); + + ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &TextureLayered::create, DEFVAL(FLAGS_DEFAULT)); + ClassDB::bind_method(D_METHOD("set_layer_data", "image", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &TextureLayered::set_data_partial, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &TextureLayered::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &TextureLayered::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + + BIND_ENUM_CONSTANT(FLAG_MIPMAPS); + BIND_ENUM_CONSTANT(FLAG_REPEAT); + BIND_ENUM_CONSTANT(FLAG_FILTER); + BIND_ENUM_CONSTANT(FLAGS_DEFAULT); +} + +TextureLayered::TextureLayered(bool p_3d) { + is_3d = p_3d; + format = Image::FORMAT_MAX; + flags = FLAGS_DEFAULT; + + width = 0; + height = 0; + depth = 0; + + texture = VS::get_singleton()->texture_create(); +} + +TextureLayered::~TextureLayered() { + if (texture.is_valid()) { + VS::get_singleton()->free(texture); + } +} + +RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + + Ref<TextureLayered> lt; + Ref<Texture3D> tex3d; + Ref<TextureArray> texarr; + + if (p_path.ends_with("tex3d")) { + tex3d.instance(); + lt = tex3d; + } else if (p_path.ends_with("texarr")) { + texarr.instance(); + lt = texarr; + } else { + ERR_EXPLAIN("Unrecognized layered texture extension"); + ERR_FAIL_V(RES()); + } + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, RES()); + + uint8_t header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + if (header[0] == 'G' && header[1] == 'D' && header[2] == '3' && header[3] == 'T') { + if (tex3d.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(tex3d.is_null(), RES()) + } + } else if (header[0] == 'G' && header[1] == 'D' && header[2] == 'A' && header[3] == 'T') { + if (texarr.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(texarr.is_null(), RES()) + } + } else { + + ERR_EXPLAIN("Unrecognized layered texture file format: " + String((const char *)header)); + ERR_FAIL_V(RES()); + } + + int tw = f->get_32(); + int th = f->get_32(); + int td = f->get_32(); + int flags = f->get_32(); //texture flags! + Image::Format format = Image::Format(f->get_32()); + uint32_t compression = f->get_32(); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + + lt->create(tw, th, td, format, flags); + + for (int layer = 0; layer < td; layer++) { + + Ref<Image> image; + image.instance(); + + if (compression == COMPRESSION_LOSSLESS) { + //look for a PNG file inside + + int mipmaps = f->get_32(); + Vector<Ref<Image> > mipmap_images; + + for (int i = 0; i < mipmaps; i++) { + uint32_t size = f->get_32(); + + PoolVector<uint8_t> pv; + pv.resize(size); + { + PoolVector<uint8_t>::Write w = pv.write(); + f->get_buffer(w.ptr(), size); + } + + Ref<Image> img = Image::lossless_unpacker(pv); + + if (img.is_null() || img->empty() || format != img->get_format()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + + mipmap_images.push_back(img); + } + + if (mipmap_images.size() == 1) { + + image = mipmap_images[0]; + + } else { + int total_size = Image::get_image_data_size(tw, th, format, true); + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + + int ofs = 0; + for (int i = 0; i < mipmap_images.size(); i++) { + + PoolVector<uint8_t> id = mipmap_images[i]->get_data(); + int len = id.size(); + PoolVector<uint8_t>::Read r = id.read(); + copymem(&w[ofs], r.ptr(), len); + ofs += len; + } + } + + image->create(tw, th, true, format, img_data); + if (image->empty()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + } + + } else { + + //look for regular format + bool mipmaps = (flags & Texture::FLAG_MIPMAPS); + int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + int bytes = f->get_buffer(w.ptr(), total_size); + if (bytes != total_size) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + memdelete(f); + } + ERR_FAIL_V(RES()); + } + } + + image->create(tw, th, mipmaps, format, img_data); + } + + lt->set_layer_data(image, layer); + } + + if (r_error) + *r_error = OK; + + return lt; +} + +void ResourceFormatLoaderTextureLayered::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("tex3d"); + p_extensions->push_back("texarr"); +} +bool ResourceFormatLoaderTextureLayered::handles_type(const String &p_type) const { + return p_type == "Texture3D" || p_type == "TextureArray"; +} +String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_path) const { + + if (p_path.get_extension().to_lower() == "tex3d") + return "Texture3D"; + if (p_path.get_extension().to_lower() == "texarr") + return "TextureArray"; + return ""; +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index c994bdad5f..1c18189b2c 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -401,6 +401,88 @@ VARIANT_ENUM_CAST(CubeMap::Flags) VARIANT_ENUM_CAST(CubeMap::Side) VARIANT_ENUM_CAST(CubeMap::Storage) +class TextureLayered : public Resource { + + GDCLASS(TextureLayered, Resource) + +public: + enum Flags { + FLAG_MIPMAPS = VisualServer::TEXTURE_FLAG_MIPMAPS, + FLAG_REPEAT = VisualServer::TEXTURE_FLAG_REPEAT, + FLAG_FILTER = VisualServer::TEXTURE_FLAG_FILTER, + FLAG_CONVERT_TO_LINEAR = VisualServer::TEXTURE_FLAG_CONVERT_TO_LINEAR, + FLAGS_DEFAULT = FLAG_FILTER, + }; + +private: + bool is_3d; + RID texture; + Image::Format format; + uint32_t flags; + + int width; + int height; + int depth; + + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + +protected: + static void _bind_methods(); + +public: + void set_flags(uint32_t p_flags); + uint32_t get_flags() const; + + Image::Format get_format() const; + uint32_t get_width() const; + uint32_t get_height() const; + uint32_t get_depth() const; + + void create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags = FLAGS_DEFAULT); + void set_layer_data(const Ref<Image> &p_image, int p_layer); + Ref<Image> get_layer_data(int p_layer) const; + void set_data_partial(const Ref<Image> &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap = 0); + + virtual RID get_rid() const; + virtual void set_path(const String &p_path, bool p_take_over = false); + + TextureLayered(bool p_3d = false); + ~TextureLayered(); +}; + +VARIANT_ENUM_CAST(TextureLayered::Flags) + +class Texture3D : public TextureLayered { + + GDCLASS(Texture3D, TextureLayered) +public: + Texture3D() : + TextureLayered(true) {} +}; + +class TextureArray : public TextureLayered { + + GDCLASS(TextureArray, TextureLayered) +public: + TextureArray() : + TextureLayered(false) {} +}; + +class ResourceFormatLoaderTextureLayered : public ResourceFormatLoader { +public: + enum Compression { + COMPRESSION_LOSSLESS, + COMPRESSION_VRAM, + COMPRESSION_UNCOMPRESSED + }; + + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + class CurveTexture : public Texture { GDCLASS(CurveTexture, Texture) diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 58057cda0c..3d2b6c36de 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -262,7 +262,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial")); p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate")); p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region")); - p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE")); + p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE")); if (tile_get_tile_mode(id) == AUTO_TILE) { p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); @@ -272,6 +272,12 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + } else if (tile_get_tile_mode(id) == ATLAS_TILE) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset")); p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D")); @@ -494,8 +500,21 @@ uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { const Map<Vector2, uint16_t> &TileSet::autotile_get_bitmask_map(int p_id) { static Map<Vector2, uint16_t> dummy; + static Map<Vector2, uint16_t> dummy_atlas; ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.flags; + if (tile_get_tile_mode(p_id) == ATLAS_TILE) { + dummy_atlas = Map<Vector2, uint16_t>(); + Rect2 region = tile_get_region(p_id); + Size2 size = autotile_get_size(p_id); + float spacing = autotile_get_spacing(p_id); + for (int x = 0; x < (region.size.x / (size.x + spacing)); x++) { + for (int y = 0; y < (region.size.y / (size.y + spacing)); y++) { + dummy_atlas.insert(Vector2(x, y), 0); + } + } + return dummy_atlas; + } else + return tile_map[p_id].autotile_data.flags; } Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node, const Vector2 &p_tile_location) { @@ -576,7 +595,7 @@ void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_sha ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].shape = p_shape; + tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape; emit_changed(); } @@ -594,7 +613,7 @@ void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].shape_transform = p_offset; + tile_map[p_id].shapes_data.write[p_shape_id].shape_transform = p_offset; emit_changed(); } @@ -622,7 +641,7 @@ void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_ ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].one_way_collision = p_one_way; + tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision = p_one_way; emit_changed(); } @@ -923,6 +942,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map); ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material); ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material); + ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate); + ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate); ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset); ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset); ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region); @@ -974,7 +995,7 @@ void TileSet::_bind_methods() { BIND_ENUM_CONSTANT(SINGLE_TILE); BIND_ENUM_CONSTANT(AUTO_TILE); - BIND_ENUM_CONSTANT(ANIMATED_TILE); + BIND_ENUM_CONSTANT(ATLAS_TILE); } TileSet::TileSet() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index ec635ee5cc..40eee2700d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -75,7 +75,7 @@ public: enum TileMode { SINGLE_TILE, AUTO_TILE, - ANIMATED_TILE + ATLAS_TILE }; struct AutotileData { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b7b7802d1b..d8fc3677fb 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -949,8 +949,8 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node); ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position); + ClassDB::bind_method(D_METHOD("get_node", "type", "id"), &VisualShader::get_node); ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position); - ClassDB::bind_method(D_METHOD("get_node", "type"), &VisualShader::get_node); ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list); ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index bf765385d0..661606c7ef 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -102,6 +102,8 @@ SceneStringNames::SceneStringNames() { _update_scroll = StaticCString::create("_update_scroll"); _update_xform = StaticCString::create("_update_xform"); + _clips_input = StaticCString::create("_clips_input"); + _proxgroup_add = StaticCString::create("_proxgroup_add"); _proxgroup_remove = StaticCString::create("_proxgroup_remove"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index b88cf7d8d7..817158f9f3 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -127,6 +127,8 @@ public: StringName _update_scroll; StringName _update_xform; + StringName _clips_input; + StringName _proxgroup_add; StringName _proxgroup_remove; diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h index 0b922c5892..910b401db9 100644 --- a/servers/arvr/arvr_interface.h +++ b/servers/arvr/arvr_interface.h @@ -88,7 +88,7 @@ public: bool is_primary(); void set_is_primary(bool p_is_primary); - virtual bool is_initialized() = 0; /* returns true if we've initialized this interface */ + virtual bool is_initialized() const = 0; /* returns true if we've initialized this interface */ void set_is_initialized(bool p_initialized); /* helper function, will call initialize or uninitialize */ virtual bool initialize() = 0; /* initialize this interface, if this has an HMD it becomes the primary interface */ virtual void uninitialize() = 0; /* deinitialize this interface */ diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp index f48bedbdac..0d1aad0dff 100644 --- a/servers/arvr_server.cpp +++ b/servers/arvr_server.cpp @@ -353,7 +353,7 @@ void ARVRServer::_process() { if (!interfaces[i].is_valid()) { // ignore, not a valid reference } else if (interfaces[i]->is_initialized()) { - interfaces[i]->process(); + interfaces.write[i]->process(); }; }; }; diff --git a/servers/audio/audio_effect.h b/servers/audio/audio_effect.h index cf732d4bdd..b950e824c0 100644 --- a/servers/audio/audio_effect.h +++ b/servers/audio/audio_effect.h @@ -39,6 +39,7 @@ class AudioEffectInstance : public Reference { public: virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) = 0; + virtual bool process_silence() const { return false; } }; class AudioEffect : public Resource { diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index 9faa4056c3..3414351681 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -100,6 +100,8 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i if (C == 6) { + // FIXME: Lot of unused assignments here, but it seems like intermediate calculations + // should be done as for C == 2 (C == 4 also has some unused assignments). float v0 = rb[(pos * 6) + 0]; float v1 = rb[(pos * 6) + 1]; float v2 = rb[(pos * 6) + 2]; diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp index 113f23f8f2..eef8aba0c4 100644 --- a/servers/audio/audio_stream.cpp +++ b/servers/audio/audio_stream.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "audio_stream.h" +#include "os/os.h" ////////////////////////////// @@ -99,6 +100,119 @@ void AudioStream::_bind_methods() { //////////////////////////////// +Ref<AudioStreamPlayback> AudioStreamMicrophone::instance_playback() { + Ref<AudioStreamPlaybackMicrophone> playback; + playback.instance(); + + playbacks.insert(playback.ptr()); + + playback->microphone = Ref<AudioStreamMicrophone>((AudioStreamMicrophone *)this); + playback->active = false; + + return playback; +} + +String AudioStreamMicrophone::get_stream_name() const { + + //if (audio_stream.is_valid()) { + //return "Random: " + audio_stream->get_name(); + //} + return "Microphone"; +} + +float AudioStreamMicrophone::get_length() const { + return 0; +} + +void AudioStreamMicrophone::_bind_methods() { +} + +AudioStreamMicrophone::AudioStreamMicrophone() { +} + +void AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) { + + AudioDriver::get_singleton()->lock(); + + Vector<int32_t> buf = AudioDriver::get_singleton()->get_input_buffer(); + unsigned int input_size = AudioDriver::get_singleton()->get_input_size(); + + // p_frames is multipled by two since an AudioFrame is stereo + if ((p_frames + MICROPHONE_PLAYBACK_DELAY * 2) > input_size) { + for (int i = 0; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0.0f, 0.0f); + } + input_ofs = 0; + } else { + for (int i = 0; i < p_frames; i++) { + if (input_size >= input_ofs) { + float l = (buf[input_ofs++] >> 16) / 32768.f; + if (input_ofs >= buf.size()) { + input_ofs = 0; + } + float r = (buf[input_ofs++] >> 16) / 32768.f; + if (input_ofs >= buf.size()) { + input_ofs = 0; + } + + p_buffer[i] = AudioFrame(l, r); + } else { + p_buffer[i] = AudioFrame(0.0f, 0.0f); + } + } + } + + AudioDriver::get_singleton()->unlock(); +} + +void AudioStreamPlaybackMicrophone::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) { + AudioStreamPlaybackResampled::mix(p_buffer, p_rate_scale, p_frames); +} + +float AudioStreamPlaybackMicrophone::get_stream_sampling_rate() { + return AudioDriver::get_singleton()->get_mix_rate(); +} + +void AudioStreamPlaybackMicrophone::start(float p_from_pos) { + input_ofs = 0; + + AudioDriver::get_singleton()->capture_start(); + + active = true; + _begin_resample(); +} + +void AudioStreamPlaybackMicrophone::stop() { + AudioDriver::get_singleton()->capture_stop(); + active = false; +} + +bool AudioStreamPlaybackMicrophone::is_playing() const { + return active; +} + +int AudioStreamPlaybackMicrophone::get_loop_count() const { + return 0; +} + +float AudioStreamPlaybackMicrophone::get_playback_position() const { + return 0; +} + +void AudioStreamPlaybackMicrophone::seek(float p_time) { + return; // Can't seek a microphone input +} + +AudioStreamPlaybackMicrophone::~AudioStreamPlaybackMicrophone() { + microphone->playbacks.erase(this); + stop(); +} + +AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() { +} + +//////////////////////////////// + void AudioStreamRandomPitch::set_audio_stream(const Ref<AudioStream> &p_audio_stream) { audio_stream = p_audio_stream; diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index 3312ce1ff6..66e1b6ee2f 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -94,6 +94,63 @@ public: virtual float get_length() const = 0; //if supported, otherwise return 0 }; +// Microphone + +class AudioStreamPlaybackMicrophone; + +class AudioStreamMicrophone : public AudioStream { + + GDCLASS(AudioStreamMicrophone, AudioStream) + friend class AudioStreamPlaybackMicrophone; + + Set<AudioStreamPlaybackMicrophone *> playbacks; + +protected: + static void _bind_methods(); + +public: + virtual Ref<AudioStreamPlayback> instance_playback(); + virtual String get_stream_name() const; + + virtual float get_length() const; //if supported, otherwise return 0 + + AudioStreamMicrophone(); +}; + +class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { + + GDCLASS(AudioStreamPlaybackMicrophone, AudioStreamPlayback) + friend class AudioStreamMicrophone; + + const int MICROPHONE_PLAYBACK_DELAY = 256; + + bool active; + unsigned int input_ofs; + + Ref<AudioStreamMicrophone> microphone; + +protected: + virtual void _mix_internal(AudioFrame *p_buffer, int p_frames); + virtual float get_stream_sampling_rate(); + +public: + virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames); + + virtual void start(float p_from_pos = 0.0); + virtual void stop(); + virtual bool is_playing() const; + + virtual int get_loop_count() const; //times it looped + + virtual float get_playback_position() const; + virtual void seek(float p_time); + + ~AudioStreamPlaybackMicrophone(); + AudioStreamPlaybackMicrophone(); +}; + +// + class AudioStreamPlaybackRandomPitch; class AudioStreamRandomPitch : public AudioStream { diff --git a/servers/audio/effects/audio_effect_chorus.cpp b/servers/audio/effects/audio_effect_chorus.cpp index f2f554a09b..fd9e3311e7 100644 --- a/servers/audio/effects/audio_effect_chorus.cpp +++ b/servers/audio/effects/audio_effect_chorus.cpp @@ -53,7 +53,7 @@ void AudioEffectChorusInstance::_process_chunk(const AudioFrame *p_src_frames, A //fill ringbuffer for (int i = 0; i < p_frame_count; i++) { - audio_buffer[(buffer_pos + i) & buffer_mask] = p_src_frames[i]; + audio_buffer.write[(buffer_pos + i) & buffer_mask] = p_src_frames[i]; p_dst_frames[i] = p_src_frames[i] * base->dry; } @@ -175,7 +175,7 @@ Ref<AudioEffectInstance> AudioEffectChorus::instance() { ins->buffer_pos = 0; ins->audio_buffer.resize(ringbuff_size); for (int i = 0; i < ringbuff_size; i++) { - ins->audio_buffer[i] = AudioFrame(0, 0); + ins->audio_buffer.write[i] = AudioFrame(0, 0); } return ins; diff --git a/servers/audio/effects/audio_effect_eq.cpp b/servers/audio/effects/audio_effect_eq.cpp index a30fca4e8d..cf8f7d3e16 100644 --- a/servers/audio/effects/audio_effect_eq.cpp +++ b/servers/audio/effects/audio_effect_eq.cpp @@ -70,7 +70,7 @@ Ref<AudioEffectInstance> AudioEffectEQ::instance() { for (int i = 0; i < 2; i++) { ins->bands[i].resize(eq.get_band_count()); for (int j = 0; j < ins->bands[i].size(); j++) { - ins->bands[i][j] = eq.get_band_processor(j); + ins->bands[i].write[j] = eq.get_band_processor(j); } } @@ -79,7 +79,7 @@ Ref<AudioEffectInstance> AudioEffectEQ::instance() { void AudioEffectEQ::set_band_gain_db(int p_band, float p_volume) { ERR_FAIL_INDEX(p_band, gain.size()); - gain[p_band] = p_volume; + gain.write[p_band] = p_volume; } float AudioEffectEQ::get_band_gain_db(int p_band) const { @@ -134,7 +134,7 @@ AudioEffectEQ::AudioEffectEQ(EQ::Preset p_preset) { eq.set_preset_band_mode(p_preset); gain.resize(eq.get_band_count()); for (int i = 0; i < gain.size(); i++) { - gain[i] = 0.0; + gain.write[i] = 0.0; String name = "band_db/" + itos(eq.get_band_frequency(i)) + "_hz"; prop_band_map[name] = i; band_names.push_back(name); diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp new file mode 100644 index 0000000000..74a6838d1a --- /dev/null +++ b/servers/audio/effects/audio_effect_record.cpp @@ -0,0 +1,264 @@ +/*************************************************************************/ +/* audio_effect_record.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "audio_effect_record.h" + +void AudioEffectRecordInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) { + if (!is_recording) { + return; + } + + //Add incoming audio frames to the IO ring buffer + const AudioFrame *src = p_src_frames; + AudioFrame *rb_buf = ring_buffer.ptrw(); + for (int i = 0; i < p_frame_count; i++) { + rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i]; + ring_buffer_pos++; + } +} + +bool AudioEffectRecordInstance::process_silence() { + return true; +} + +void AudioEffectRecordInstance::_io_thread_process() { + + //Reset recorder status + thread_active = true; + ring_buffer_pos = 0; + ring_buffer_read_pos = 0; + + //We start a new recording + recording_data.resize(0); //Clear data completely and reset length + is_recording = true; + + while (is_recording) { + //Check: The current recording has been requested to stop + if (is_recording && !base->recording_active) { + is_recording = false; + } + + //Case: Frames are remaining in the buffer + if (ring_buffer_read_pos < ring_buffer_pos) { + //Read from the buffer into recording_data + _io_store_buffer(); + } + //Case: The buffer is empty + else if (is_recording) { + //Wait to avoid too much busy-wait + OS::get_singleton()->delay_usec(500); + } + } + + thread_active = false; +} + +void AudioEffectRecordInstance::_io_store_buffer() { + int to_read = ring_buffer_pos - ring_buffer_read_pos; + + AudioFrame *rb_buf = ring_buffer.ptrw(); + + while (to_read) { + AudioFrame buffered_frame = rb_buf[ring_buffer_read_pos & ring_buffer_mask]; + recording_data.push_back(buffered_frame.l); + recording_data.push_back(buffered_frame.r); + + ring_buffer_read_pos++; + to_read--; + } +} + +void AudioEffectRecordInstance::_thread_callback(void *_instance) { + + AudioEffectRecordInstance *aeri = reinterpret_cast<AudioEffectRecordInstance *>(_instance); + + aeri->_io_thread_process(); +} + +void AudioEffectRecordInstance::init() { + io_thread = Thread::create(_thread_callback, this); +} + +Ref<AudioEffectInstance> AudioEffectRecord::instance() { + Ref<AudioEffectRecordInstance> ins; + ins.instance(); + ins->base = Ref<AudioEffectRecord>(this); + ins->is_recording = false; + + //Re-using the buffer size calculations from audio_effect_delay.cpp + float ring_buffer_max_size = IO_BUFFER_SIZE_MS; + ring_buffer_max_size /= 1000.0; //convert to seconds + ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate(); + + int ringbuff_size = ring_buffer_max_size; + + int bits = 0; + + while (ringbuff_size > 0) { + bits++; + ringbuff_size /= 2; + } + + ringbuff_size = 1 << bits; + ins->ring_buffer_mask = ringbuff_size - 1; + ins->ring_buffer_pos = 0; + + ins->ring_buffer.resize(ringbuff_size); + + ins->ring_buffer_read_pos = 0; + + ensure_thread_stopped(); + current_instance = ins; + if (recording_active) { + ins->init(); + } + + return ins; +} + +void AudioEffectRecord::ensure_thread_stopped() { + recording_active = false; + if (current_instance != 0 && current_instance->thread_active) { + Thread::wait_to_finish(current_instance->io_thread); + } +} + +void AudioEffectRecord::set_recording_active(bool p_record) { + if (p_record) { + if (current_instance == 0) { + WARN_PRINTS("Recording should not be set as active before Godot has initialized."); + recording_active = false; + return; + } + + ensure_thread_stopped(); + current_instance->init(); + } + + recording_active = p_record; +} + +bool AudioEffectRecord::is_recording_active() const { + return recording_active; +} + +void AudioEffectRecord::set_format(AudioStreamSample::Format p_format) { + format = p_format; +} + +AudioStreamSample::Format AudioEffectRecord::get_format() const { + return format; +} + +Ref<AudioStreamSample> AudioEffectRecord::get_recording() const { + AudioStreamSample::Format dst_format = format; + bool stereo = true; //forcing mono is not implemented + + PoolVector<uint8_t> dst_data; + + if (dst_format == AudioStreamSample::FORMAT_8_BITS) { + int data_size = current_instance->recording_data.size(); + dst_data.resize(data_size); + PoolVector<uint8_t>::Write w = dst_data.write(); + + for (int i = 0; i < data_size; i++) { + int8_t v = CLAMP(current_instance->recording_data[i] * 128, -128, 127); + w[i] = v; + } + } else if (dst_format == AudioStreamSample::FORMAT_16_BITS) { + int data_size = current_instance->recording_data.size(); + dst_data.resize(data_size * 2); + PoolVector<uint8_t>::Write w = dst_data.write(); + + for (int i = 0; i < data_size; i++) { + int16_t v = CLAMP(current_instance->recording_data[i] * 32768, -32768, 32767); + encode_uint16(v, &w[i * 2]); + } + } else if (dst_format == AudioStreamSample::FORMAT_IMA_ADPCM) { + //byte interleave + Vector<float> left; + Vector<float> right; + + int tframes = current_instance->recording_data.size() / 2; + left.resize(tframes); + right.resize(tframes); + + for (int i = 0; i < tframes; i++) { + left.set(i, current_instance->recording_data[i * 2 + 0]); + right.set(i, current_instance->recording_data[i * 2 + 1]); + } + + PoolVector<uint8_t> bleft; + PoolVector<uint8_t> bright; + + ResourceImporterWAV::_compress_ima_adpcm(left, bleft); + ResourceImporterWAV::_compress_ima_adpcm(right, bright); + + int dl = bleft.size(); + dst_data.resize(dl * 2); + + PoolVector<uint8_t>::Write w = dst_data.write(); + PoolVector<uint8_t>::Read rl = bleft.read(); + PoolVector<uint8_t>::Read rr = bright.read(); + + for (int i = 0; i < dl; i++) { + w[i * 2 + 0] = rl[i]; + w[i * 2 + 1] = rr[i]; + } + } else { + ERR_EXPLAIN("format not implemented"); + } + + Ref<AudioStreamSample> sample; + sample.instance(); + sample->set_data(dst_data); + sample->set_format(dst_format); + sample->set_mix_rate(AudioServer::get_singleton()->get_mix_rate()); + sample->set_loop_mode(AudioStreamSample::LOOP_DISABLED); + sample->set_loop_begin(0); + sample->set_loop_end(0); + sample->set_stereo(stereo); + + return sample; +} + +void AudioEffectRecord::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_recording_active", "record"), &AudioEffectRecord::set_recording_active); + ClassDB::bind_method(D_METHOD("is_recording_active"), &AudioEffectRecord::is_recording_active); + ClassDB::bind_method(D_METHOD("set_format", "format"), &AudioEffectRecord::set_format); + ClassDB::bind_method(D_METHOD("get_format"), &AudioEffectRecord::get_format); + ClassDB::bind_method(D_METHOD("get_recording"), &AudioEffectRecord::get_recording); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); +} + +AudioEffectRecord::AudioEffectRecord() { + format = AudioStreamSample::FORMAT_16_BITS; +} diff --git a/servers/audio/effects/audio_effect_record.h b/servers/audio/effects/audio_effect_record.h new file mode 100644 index 0000000000..e4f5ba8a23 --- /dev/null +++ b/servers/audio/effects/audio_effect_record.h @@ -0,0 +1,103 @@ +/*************************************************************************/ +/* audio_effect_record.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef AUDIOEFFECTRECORD_H +#define AUDIOEFFECTRECORD_H + +#include "core/os/thread.h" +#include "editor/import/resource_importer_wav.h" +#include "io/marshalls.h" +#include "os/file_access.h" +#include "os/os.h" +#include "scene/resources/audio_stream_sample.h" +#include "servers/audio/audio_effect.h" +#include "servers/audio_server.h" + +class AudioEffectRecord; + +class AudioEffectRecordInstance : public AudioEffectInstance { + GDCLASS(AudioEffectRecordInstance, AudioEffectInstance) + friend class AudioEffectRecord; + Ref<AudioEffectRecord> base; + + bool is_recording; + Thread *io_thread; + bool thread_active = false; + + Vector<AudioFrame> ring_buffer; + Vector<float> recording_data; + + unsigned int ring_buffer_pos; + unsigned int ring_buffer_mask; + unsigned int ring_buffer_read_pos; + + void _io_thread_process(); + void _io_store_buffer(); + static void _thread_callback(void *_instance); + void _init_recording(); + +public: + void init(); + virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count); + virtual bool process_silence(); +}; + +class AudioEffectRecord : public AudioEffect { + GDCLASS(AudioEffectRecord, AudioEffect) + + friend class AudioEffectRecordInstance; + + enum { + IO_BUFFER_SIZE_MS = 1500 + }; + + bool recording_active; + Ref<AudioEffectRecordInstance> current_instance; + + AudioStreamSample::Format format; + + void ensure_thread_stopped(); + +protected: + static void _bind_methods(); + static void debug(uint64_t time_diff, int p_frame_count); + +public: + Ref<AudioEffectInstance> instance(); + void set_recording_active(bool p_record); + bool is_recording_active() const; + void set_format(AudioStreamSample::Format p_format); + AudioStreamSample::Format get_format() const; + Ref<AudioStreamSample> get_recording() const; + + AudioEffectRecord(); +}; + +#endif // AUDIOEFFECTRECORD_H diff --git a/servers/audio/effects/eq.cpp b/servers/audio/effects/eq.cpp index 9ef41191f5..b15fc7ecf4 100644 --- a/servers/audio/effects/eq.cpp +++ b/servers/audio/effects/eq.cpp @@ -108,9 +108,9 @@ void EQ::recalculate_band_coefficients() { ERR_CONTINUE(roots == 0); - band[i].c1 = 2.0 * ((0.5 - r1) / 2.0); - band[i].c2 = 2.0 * r1; - band[i].c3 = 2.0 * (0.5 + r1) * cos(th); + band.write[i].c1 = 2.0 * ((0.5 - r1) / 2.0); + band.write[i].c2 = 2.0 * r1; + band.write[i].c3 = 2.0 * (0.5 + r1) * cos(th); //printf("band %i, coefs = %f,%f,%f\n",i,(float)bands[i].c1,(float)bands[i].c2,(float)bands[i].c3); } } @@ -180,7 +180,7 @@ void EQ::set_bands(const Vector<float> &p_bands) { band.resize(p_bands.size()); for (int i = 0; i < p_bands.size(); i++) { - band[i].freq = p_bands[i]; + band.write[i].freq = p_bands[i]; } recalculate_band_coefficients(); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index ceb843c031..14318f282b 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -33,6 +33,7 @@ #include "os/file_access.h" #include "os/os.h" #include "project_settings.h" +#include "scene/resources/audio_stream_sample.h" #include "servers/audio/audio_driver_dummy.h" #include "servers/audio/effects/audio_effect_compressor.h" #ifdef TOOLS_ENABLED @@ -79,6 +80,17 @@ double AudioDriver::get_mix_time() const { return total; } +void AudioDriver::input_buffer_write(int32_t sample) { + + input_buffer.write[input_position++] = sample; + if (input_position >= input_buffer.size()) { + input_position = 0; + } + if (input_size < input_buffer.size()) { + input_size++; + } +} + AudioDriver::SpeakerMode AudioDriver::get_speaker_mode_by_total_channels(int p_channels) const { switch (p_channels) { case 4: return SPEAKER_SURROUND_31; @@ -113,6 +125,14 @@ String AudioDriver::get_device() { return "Default"; } +Array AudioDriver::capture_get_device_list() { + Array list; + + list.push_back("Default"); + + return list; +} + AudioDriver::AudioDriver() { _last_mix_time = 0; @@ -264,7 +284,7 @@ void AudioServer::_mix_step() { bus->index_cache = i; //might be moved around by editor, so.. for (int k = 0; k < bus->channels.size(); k++) { - bus->channels[k].used = false; + bus->channels.write[k].used = false; } if (bus->solo) { @@ -310,7 +330,7 @@ void AudioServer::_mix_step() { if (bus->channels[k].active && !bus->channels[k].used) { //buffer was not used, but it's still active, so it must be cleaned - AudioFrame *buf = bus->channels[k].buffer.ptrw(); + AudioFrame *buf = bus->channels.write[k].buffer.ptrw(); for (uint32_t j = 0; j < buffer_size; j++) { @@ -332,21 +352,21 @@ void AudioServer::_mix_step() { for (int k = 0; k < bus->channels.size(); k++) { - if (!bus->channels[k].active) + if (!(bus->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) continue; - bus->channels[k].effect_instances[j]->process(bus->channels[k].buffer.ptr(), temp_buffer[k].ptrw(), buffer_size); + bus->channels.write[k].effect_instances.write[j]->process(bus->channels[k].buffer.ptr(), temp_buffer.write[k].ptrw(), buffer_size); } //swap buffers, so internal buffer always has the right data for (int k = 0; k < bus->channels.size(); k++) { - if (!buses[i]->channels[k].active) + if (!(buses[i]->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) continue; - SWAP(bus->channels[k].buffer, temp_buffer[k]); + SWAP(bus->channels.write[k].buffer, temp_buffer.write[k]); } #ifdef DEBUG_ENABLED - bus->effects[j].prof_time += OS::get_singleton()->get_ticks_usec() - ticks; + bus->effects.write[j].prof_time += OS::get_singleton()->get_ticks_usec() - ticks; #endif } } @@ -372,7 +392,7 @@ void AudioServer::_mix_step() { if (!bus->channels[k].active) continue; - AudioFrame *buf = bus->channels[k].buffer.ptrw(); + AudioFrame *buf = bus->channels.write[k].buffer.ptrw(); AudioFrame peak = AudioFrame(0, 0); @@ -403,15 +423,15 @@ void AudioServer::_mix_step() { } } - bus->channels[k].peak_volume = AudioFrame(Math::linear2db(peak.l + 0.0000000001), Math::linear2db(peak.r + 0.0000000001)); + bus->channels.write[k].peak_volume = AudioFrame(Math::linear2db(peak.l + 0.0000000001), Math::linear2db(peak.r + 0.0000000001)); if (!bus->channels[k].used) { //see if any audio is contained, because channel was not used if (MAX(peak.r, peak.l) > Math::db2linear(channel_disable_threshold_db)) { - bus->channels[k].last_mix_with_audio = mix_frames; + bus->channels.write[k].last_mix_with_audio = mix_frames; } else if (mix_frames - bus->channels[k].last_mix_with_audio > channel_disable_frames) { - bus->channels[k].active = false; + bus->channels.write[k].active = false; continue; //went inactive, don't mix. } } @@ -436,12 +456,12 @@ AudioFrame *AudioServer::thread_get_channel_mix_buffer(int p_bus, int p_buffer) ERR_FAIL_INDEX_V(p_bus, buses.size(), NULL); ERR_FAIL_INDEX_V(p_buffer, buses[p_bus]->channels.size(), NULL); - AudioFrame *data = buses[p_bus]->channels[p_buffer].buffer.ptrw(); + AudioFrame *data = buses.write[p_bus]->channels.write[p_buffer].buffer.ptrw(); if (!buses[p_bus]->channels[p_buffer].used) { - buses[p_bus]->channels[p_buffer].used = true; - buses[p_bus]->channels[p_buffer].active = true; - buses[p_bus]->channels[p_buffer].last_mix_with_audio = mix_frames; + buses.write[p_bus]->channels.write[p_buffer].used = true; + buses.write[p_bus]->channels.write[p_buffer].active = true; + buses.write[p_bus]->channels.write[p_buffer].last_mix_with_audio = mix_frames; for (uint32_t i = 0; i < buffer_size; i++) { data[i] = AudioFrame(0, 0); } @@ -506,10 +526,10 @@ void AudioServer::set_bus_count(int p_count) { } } - buses[i] = memnew(Bus); - buses[i]->channels.resize(channel_count); + buses.write[i] = memnew(Bus); + buses.write[i]->channels.resize(channel_count); for (int j = 0; j < channel_count; j++) { - buses[i]->channels[j].buffer.resize(buffer_size); + buses.write[i]->channels.write[j].buffer.resize(buffer_size); } buses[i]->name = attempt; buses[i]->solo = false; @@ -581,7 +601,7 @@ void AudioServer::add_bus(int p_at_pos) { Bus *bus = memnew(Bus); bus->channels.resize(channel_count); for (int j = 0; j < channel_count; j++) { - bus->channels[j].buffer.resize(buffer_size); + bus->channels.write[j].buffer.resize(buffer_size); } bus->name = attempt; bus->solo = false; @@ -764,13 +784,13 @@ bool AudioServer::is_bus_bypassing_effects(int p_bus) const { void AudioServer::_update_bus_effects(int p_bus) { for (int i = 0; i < buses[p_bus]->channels.size(); i++) { - buses[p_bus]->channels[i].effect_instances.resize(buses[p_bus]->effects.size()); + buses.write[p_bus]->channels.write[i].effect_instances.resize(buses[p_bus]->effects.size()); for (int j = 0; j < buses[p_bus]->effects.size(); j++) { - Ref<AudioEffectInstance> fx = buses[p_bus]->effects[j].effect->instance(); + Ref<AudioEffectInstance> fx = buses.write[p_bus]->effects.write[j].effect->instance(); if (Object::cast_to<AudioEffectCompressorInstance>(*fx)) { Object::cast_to<AudioEffectCompressorInstance>(*fx)->set_current_channel(i); } - buses[p_bus]->channels[i].effect_instances[j] = fx; + buses.write[p_bus]->channels.write[i].effect_instances.write[j] = fx; } } } @@ -841,7 +861,7 @@ void AudioServer::swap_bus_effects(int p_bus, int p_effect, int p_by_effect) { MARK_EDITED lock(); - SWAP(buses[p_bus]->effects[p_effect], buses[p_bus]->effects[p_by_effect]); + SWAP(buses.write[p_bus]->effects.write[p_effect], buses.write[p_bus]->effects.write[p_by_effect]); _update_bus_effects(p_bus); unlock(); } @@ -853,7 +873,7 @@ void AudioServer::set_bus_effect_enabled(int p_bus, int p_effect, bool p_enabled MARK_EDITED - buses[p_bus]->effects[p_effect].enabled = p_enabled; + buses.write[p_bus]->effects.write[p_effect].enabled = p_enabled; } bool AudioServer::is_bus_effect_enabled(int p_bus, int p_effect) const { @@ -890,13 +910,13 @@ void AudioServer::init_channels_and_buffers() { temp_buffer.resize(channel_count); for (int i = 0; i < temp_buffer.size(); i++) { - temp_buffer[i].resize(buffer_size); + temp_buffer.write[i].resize(buffer_size); } for (int i = 0; i < buses.size(); i++) { buses[i]->channels.resize(channel_count); for (int j = 0; j < channel_count; j++) { - buses[i]->channels[j].buffer.resize(buffer_size); + buses.write[i]->channels.write[j].buffer.resize(buffer_size); } } } @@ -976,7 +996,7 @@ void AudioServer::update() { if (!bus->effects[j].enabled) continue; - bus->effects[j].prof_time = 0; + bus->effects.write[j].prof_time = 0; } } @@ -1146,11 +1166,11 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) { } bus_map[bus->name] = bus; - buses[i] = bus; + buses.write[i] = bus; buses[i]->channels.resize(channel_count); for (int j = 0; j < channel_count; j++) { - buses[i]->channels[j].buffer.resize(buffer_size); + buses.write[i]->channels.write[j].buffer.resize(buffer_size); } _update_bus_effects(i); } @@ -1169,17 +1189,17 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const { for (int i = 0; i < buses.size(); i++) { - state->buses[i].name = buses[i]->name; - state->buses[i].send = buses[i]->send; - state->buses[i].mute = buses[i]->mute; - state->buses[i].solo = buses[i]->solo; - state->buses[i].bypass = buses[i]->bypass; - state->buses[i].volume_db = buses[i]->volume_db; + state->buses.write[i].name = buses[i]->name; + state->buses.write[i].send = buses[i]->send; + state->buses.write[i].mute = buses[i]->mute; + state->buses.write[i].solo = buses[i]->solo; + state->buses.write[i].bypass = buses[i]->bypass; + state->buses.write[i].volume_db = buses[i]->volume_db; for (int j = 0; j < buses[i]->effects.size(); j++) { AudioBusLayout::Bus::Effect fx; fx.effect = buses[i]->effects[j].effect; fx.enabled = buses[i]->effects[j].enabled; - state->buses[i].effects.push_back(fx); + state->buses.write[i].effects.push_back(fx); } } @@ -1201,6 +1221,21 @@ void AudioServer::set_device(String device) { AudioDriver::get_singleton()->set_device(device); } +Array AudioServer::capture_get_device_list() { + + return AudioDriver::get_singleton()->capture_get_device_list(); +} + +String AudioServer::capture_get_device() { + + return AudioDriver::get_singleton()->capture_get_device(); +} + +void AudioServer::capture_set_device(const String &p_name) { + + AudioDriver::get_singleton()->capture_set_device(p_name); +} + void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bus_count", "amount"), &AudioServer::set_bus_count); @@ -1251,6 +1286,10 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device); ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device); + ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list); + ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device); + ClassDB::bind_method(D_METHOD("capture_set_device"), &AudioServer::capture_set_device); + ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout); ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout); @@ -1294,7 +1333,7 @@ bool AudioBusLayout::_set(const StringName &p_name, const Variant &p_value) { buses.resize(index + 1); } - Bus &bus = buses[index]; + Bus &bus = buses.write[index]; String what = s.get_slice("/", 2); @@ -1316,7 +1355,7 @@ bool AudioBusLayout::_set(const StringName &p_name, const Variant &p_value) { bus.effects.resize(which + 1); } - Bus::Effect &fx = bus.effects[which]; + Bus::Effect &fx = bus.effects.write[which]; String fxwhat = s.get_slice("/", 4); if (fxwhat == "effect") { @@ -1410,5 +1449,5 @@ void AudioBusLayout::_get_property_list(List<PropertyInfo> *p_list) const { AudioBusLayout::AudioBusLayout() { buses.resize(1); - buses[0].name = "Master"; + buses.write[0].name = "Master"; } diff --git a/servers/audio_server.h b/servers/audio_server.h index 258fd1d9b0..2663a0f968 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -38,6 +38,8 @@ #include "variant.h" class AudioDriverDummy; +class AudioStream; +class AudioStreamSample; class AudioDriver { @@ -51,8 +53,13 @@ class AudioDriver { #endif protected: + Vector<int32_t> input_buffer; + unsigned int input_position; + unsigned int input_size; + void audio_server_process(int p_frames, int32_t *p_buffer, bool p_update_mix_time = true); void update_mix_time(int p_frames); + void input_buffer_write(int32_t sample); #ifdef DEBUG_ENABLED _FORCE_INLINE_ void start_counting_ticks() { prof_ticks = OS::get_singleton()->get_ticks_usec(); } @@ -91,11 +98,21 @@ public: virtual void unlock() = 0; virtual void finish() = 0; + virtual Error capture_start() { return FAILED; } + virtual Error capture_stop() { return FAILED; } + virtual void capture_set_device(const String &p_name) {} + virtual String capture_get_device() { return "Default"; } + virtual Array capture_get_device_list(); // TODO: convert this and get_device_list to PoolStringArray + virtual float get_latency() { return 0; } SpeakerMode get_speaker_mode_by_total_channels(int p_channels) const; int get_total_channels_by_speaker_mode(SpeakerMode) const; + Vector<int32_t> get_input_buffer() { return input_buffer; } + unsigned int get_input_position() { return input_position; } + unsigned int get_input_size() { return input_size; } + #ifdef DEBUG_ENABLED uint64_t get_profiling_time() const { return prof_time; } void reset_profiling_time() { prof_time = 0; } @@ -222,6 +239,18 @@ private: void _mix_step(); +#if 0 + struct AudioInBlock { + + Ref<AudioStreamSample> audio_stream; + int current_position; + bool loops; + }; + + Map<StringName, AudioInBlock *> audio_in_block_map; + Vector<AudioInBlock *> audio_in_blocks; +#endif + struct CallbackItem { AudioCallback callback; @@ -335,8 +364,11 @@ public: String get_device(); void set_device(String device); - float get_output_latency() { return output_latency; } + Array capture_get_device_list(); + String capture_get_device(); + void capture_set_device(const String &p_name); + float get_output_latency() { return output_latency; } AudioServer(); virtual ~AudioServer(); }; diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp index 2a6a9e08ae..0ce38e4486 100644 --- a/servers/physics/body_pair_sw.cpp +++ b/servers/physics/body_pair_sw.cpp @@ -211,6 +211,14 @@ bool BodyPairSW::_test_ccd(real_t p_step, BodySW *p_A, int p_shape_A, const Tran return true; } +real_t combine_bounce(BodySW *A, BodySW *B) { + return CLAMP(A->get_bounce() + B->get_bounce(), 0, 1); +} + +real_t combine_friction(BodySW *A, BodySW *B) { + return ABS(MIN(A->get_friction(), B->get_friction())); +} + bool BodyPairSW::setup(real_t p_step) { //cannot collide @@ -331,7 +339,7 @@ bool BodyPairSW::setup(real_t p_step) { c.acc_bias_impulse = 0; c.acc_bias_impulse_center_of_mass = 0; - c.bounce = MAX(A->get_bounce(), B->get_bounce()); + c.bounce = combine_bounce(A, B); if (c.bounce) { Vector3 crA = A->get_angular_velocity().cross(c.rA); @@ -421,7 +429,7 @@ void BodyPairSW::solve(real_t p_step) { //friction impulse - real_t friction = A->get_friction() * B->get_friction(); + real_t friction = combine_friction(A, B); Vector3 lvA = A->get_linear_velocity() + A->get_angular_velocity().cross(c.rA); Vector3 lvB = B->get_linear_velocity() + B->get_angular_velocity().cross(c.rB); diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h index fd2ab16b84..9d7b147fd6 100644 --- a/servers/physics/body_sw.h +++ b/servers/physics/body_sw.h @@ -157,7 +157,7 @@ public: _FORCE_INLINE_ void add_area(AreaSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { - areas[index].refCount += 1; + areas.write[index].refCount += 1; } else { areas.ordered_insert(AreaCMP(p_area)); } @@ -166,7 +166,7 @@ public: _FORCE_INLINE_ void remove_area(AreaSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { - areas[index].refCount -= 1; + areas.write[index].refCount -= 1; if (areas[index].refCount < 1) areas.remove(index); } @@ -217,6 +217,10 @@ public: _FORCE_INLINE_ const Vector3 &get_biased_linear_velocity() const { return biased_linear_velocity; } _FORCE_INLINE_ const Vector3 &get_biased_angular_velocity() const { return biased_angular_velocity; } + _FORCE_INLINE_ void apply_central_impulse(const Vector3 &p_j) { + linear_velocity += p_j * _inv_mass; + } + _FORCE_INLINE_ void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) { linear_velocity += p_j * _inv_mass; @@ -344,7 +348,7 @@ void BodySW::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_norm if (c_max == 0) return; - Contact *c = &contacts[0]; + Contact *c = contacts.ptrw(); int idx = -1; @@ -413,6 +417,7 @@ public: virtual void add_central_force(const Vector3 &p_force) { body->add_central_force(p_force); } virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos) { body->add_force(p_force, p_pos); } virtual void add_torque(const Vector3 &p_torque) { body->add_torque(p_torque); } + virtual void apply_central_impulse(const Vector3 &p_j) { body->apply_central_impulse(p_j); } virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) { body->apply_impulse(p_pos, p_j); } virtual void apply_torque_impulse(const Vector3 &p_j) { body->apply_torque_impulse(p_j); } @@ -429,6 +434,9 @@ public: ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); return body->contacts[p_contact_idx].local_normal; } + virtual float get_contact_impulse(int p_contact_idx) const { + return 0.0f; // Only implemented for bullet + } virtual int get_contact_local_shape(int p_contact_idx) const { ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); return body->contacts[p_contact_idx].local_shape; diff --git a/servers/physics/collision_object_sw.cpp b/servers/physics/collision_object_sw.cpp index f7a58a9cf2..09f72ff39b 100644 --- a/servers/physics/collision_object_sw.cpp +++ b/servers/physics/collision_object_sw.cpp @@ -53,7 +53,7 @@ void CollisionObjectSW::set_shape(int p_index, ShapeSW *p_shape) { ERR_FAIL_INDEX(p_index, shapes.size()); shapes[p_index].shape->remove_owner(this); - shapes[p_index].shape = p_shape; + shapes.write[p_index].shape = p_shape; p_shape->add_owner(this); if (!pending_shape_update_list.in_list()) { @@ -66,8 +66,8 @@ void CollisionObjectSW::set_shape_transform(int p_index, const Transform &p_tran ERR_FAIL_INDEX(p_index, shapes.size()); - shapes[p_index].xform = p_transform; - shapes[p_index].xform_inv = p_transform.affine_inverse(); + shapes.write[p_index].xform = p_transform; + shapes.write[p_index].xform_inv = p_transform.affine_inverse(); if (!pending_shape_update_list.in_list()) { PhysicsServerSW::singleton->pending_shape_update_list.add(&pending_shape_update_list); } @@ -97,7 +97,7 @@ void CollisionObjectSW::remove_shape(int p_index) { continue; //should never get here with a null owner space->get_broadphase()->remove(shapes[i].bpid); - shapes[i].bpid = 0; + shapes.write[i].bpid = 0; } shapes[p_index].shape->remove_owner(this); shapes.remove(p_index); @@ -117,7 +117,7 @@ void CollisionObjectSW::_set_static(bool p_static) { if (!space) return; for (int i = 0; i < get_shape_count(); i++) { - Shape &s = shapes[i]; + const Shape &s = shapes[i]; if (s.bpid > 0) { space->get_broadphase()->set_static(s.bpid, _static); } @@ -128,7 +128,7 @@ void CollisionObjectSW::_unregister_shapes() { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid > 0) { space->get_broadphase()->remove(s.bpid); s.bpid = 0; @@ -143,7 +143,7 @@ void CollisionObjectSW::_update_shapes() { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid == 0) { s.bpid = space->get_broadphase()->create(this, i); space->get_broadphase()->set_static(s.bpid, _static); @@ -170,7 +170,7 @@ void CollisionObjectSW::_update_shapes_with_motion(const Vector3 &p_motion) { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid == 0) { s.bpid = space->get_broadphase()->create(this, i); space->get_broadphase()->set_static(s.bpid, _static); @@ -195,7 +195,7 @@ void CollisionObjectSW::_set_space(SpaceSW *p_space) { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid) { space->get_broadphase()->remove(s.bpid); s.bpid = 0; diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h index dee28bb6df..b6430b38dc 100644 --- a/servers/physics/collision_object_sw.h +++ b/servers/physics/collision_object_sw.h @@ -135,7 +135,7 @@ public: _FORCE_INLINE_ void set_ray_pickable(bool p_enable) { ray_pickable = p_enable; } _FORCE_INLINE_ bool is_ray_pickable() const { return ray_pickable; } - _FORCE_INLINE_ void set_shape_as_disabled(int p_idx, bool p_enable) { shapes[p_idx].disabled = p_enable; } + _FORCE_INLINE_ void set_shape_as_disabled(int p_idx, bool p_enable) { shapes.write[p_idx].disabled = p_enable; } _FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const { return shapes[p_idx].disabled; } _FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; } diff --git a/servers/physics/collision_solver_sat.cpp b/servers/physics/collision_solver_sat.cpp index e587485fcb..b059c20c95 100644 --- a/servers/physics/collision_solver_sat.cpp +++ b/servers/physics/collision_solver_sat.cpp @@ -217,8 +217,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_ // generate contacts //Plane plane_A(p_points_A[0],p_points_A[1],p_points_A[2]); - int added = 0; - for (int i = 0; i < clipbuf_len; i++) { real_t d = plane_B.distance_to(clipbuf_src[i]); @@ -233,7 +231,6 @@ static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_ continue; p_callback->call(clipbuf_src[i], closest_B); - added++; } } @@ -351,7 +348,9 @@ public: //use the smallest depth - min_B = -min_B; + if (min_B < 0.0) { // could be +0.0, we don't want it to become -0.0 + min_B = -min_B; + } if (max_B < min_B) { if (max_B < best_depth) { @@ -561,6 +560,12 @@ static void _collision_sphere_capsule(const ShapeSW *p_a, const Transform &p_tra } template <bool withMargin> +static void _collision_sphere_cylinder(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> static void _collision_sphere_convex_polygon(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { const SphereShapeSW *sphere_A = static_cast<const SphereShapeSW *>(p_a); @@ -852,6 +857,12 @@ static void _collision_box_capsule(const ShapeSW *p_a, const Transform &p_transf } template <bool withMargin> +static void _collision_box_cylinder(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> static void _collision_box_convex_polygon(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { const BoxShapeSW *box_A = static_cast<const BoxShapeSW *>(p_a); @@ -1128,6 +1139,12 @@ static void _collision_capsule_capsule(const ShapeSW *p_a, const Transform &p_tr } template <bool withMargin> +static void _collision_capsule_cylinder(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> static void _collision_capsule_convex_polygon(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { const CapsuleShapeSW *capsule_A = static_cast<const CapsuleShapeSW *>(p_a); @@ -1248,6 +1265,24 @@ static void _collision_capsule_face(const ShapeSW *p_a, const Transform &p_trans } template <bool withMargin> +static void _collision_cylinder_cylinder(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> +static void _collision_cylinder_convex_polygon(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> +static void _collision_cylinder_face(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { + + return; +} + +template <bool withMargin> static void _collision_convex_polygon_convex_polygon(const ShapeSW *p_a, const Transform &p_transform_a, const ShapeSW *p_b, const Transform &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) { const ConvexPolygonShapeSW *convex_polygon_A = static_cast<const ConvexPolygonShapeSW *>(p_a); @@ -1476,59 +1511,81 @@ bool sat_calculate_penetration(const ShapeSW *p_shape_A, const Transform &p_tran ERR_FAIL_COND_V(type_B == PhysicsServer::SHAPE_RAY, false); ERR_FAIL_COND_V(p_shape_B->is_concave(), false); - static const CollisionFunc collision_table[5][5] = { + static const CollisionFunc collision_table[6][6] = { { _collision_sphere_sphere<false>, _collision_sphere_box<false>, _collision_sphere_capsule<false>, + _collision_sphere_cylinder<false>, _collision_sphere_convex_polygon<false>, _collision_sphere_face<false> }, { 0, _collision_box_box<false>, _collision_box_capsule<false>, + _collision_box_cylinder<false>, _collision_box_convex_polygon<false>, _collision_box_face<false> }, { 0, 0, _collision_capsule_capsule<false>, + _collision_capsule_cylinder<false>, _collision_capsule_convex_polygon<false>, _collision_capsule_face<false> }, { 0, 0, 0, + _collision_cylinder_cylinder<false>, + _collision_cylinder_convex_polygon<false>, + _collision_cylinder_face<false> }, + { 0, + 0, + 0, + 0, _collision_convex_polygon_convex_polygon<false>, _collision_convex_polygon_face<false> }, { 0, 0, 0, 0, + 0, 0 }, }; - static const CollisionFunc collision_table_margin[5][5] = { + static const CollisionFunc collision_table_margin[6][6] = { { _collision_sphere_sphere<true>, _collision_sphere_box<true>, _collision_sphere_capsule<true>, + _collision_sphere_cylinder<true>, _collision_sphere_convex_polygon<true>, _collision_sphere_face<true> }, { 0, _collision_box_box<true>, _collision_box_capsule<true>, + _collision_box_cylinder<true>, _collision_box_convex_polygon<true>, _collision_box_face<true> }, { 0, 0, _collision_capsule_capsule<true>, + _collision_capsule_cylinder<true>, _collision_capsule_convex_polygon<true>, _collision_capsule_face<true> }, { 0, 0, 0, + _collision_cylinder_cylinder<true>, + _collision_cylinder_convex_polygon<true>, + _collision_cylinder_face<true> }, + { 0, + 0, + 0, + 0, _collision_convex_polygon_convex_polygon<true>, _collision_convex_polygon_face<true> }, { 0, 0, 0, 0, + 0, 0 }, }; diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp index 593218dd5d..3a32c46a9b 100644 --- a/servers/physics/physics_server_sw.cpp +++ b/servers/physics/physics_server_sw.cpp @@ -763,6 +763,40 @@ Vector3 PhysicsServerSW::body_get_applied_torque(RID p_body) const { return body->get_applied_torque(); }; +void PhysicsServerSW::body_add_central_force(RID p_body, const Vector3 &p_force) { + BodySW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->add_central_force(p_force); + body->wakeup(); +} + +void PhysicsServerSW::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos) { + BodySW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->add_force(p_force, p_pos); + body->wakeup(); +}; + +void PhysicsServerSW::body_add_torque(RID p_body, const Vector3 &p_torque) { + BodySW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->add_torque(p_torque); + body->wakeup(); +}; + +void PhysicsServerSW::body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) { + BodySW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + _update_shapes(); + + body->apply_central_impulse(p_impulse); + body->wakeup(); +} + void PhysicsServerSW::body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) { BodySW *body = body_owner.get(p_body); diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h index 3f56ba26d0..1c5754124d 100644 --- a/servers/physics/physics_server_sw.h +++ b/servers/physics/physics_server_sw.h @@ -200,6 +200,11 @@ public: virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque); virtual Vector3 body_get_applied_torque(RID p_body) const; + virtual void body_add_central_force(RID p_body, const Vector3 &p_force); + virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos); + virtual void body_add_torque(RID p_body, const Vector3 &p_torque); + + virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse); virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse); virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse); virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity); @@ -230,6 +235,72 @@ public: // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body); + /* SOFT BODY */ + + virtual RID soft_body_create(bool p_init_sleeping = false) { return RID(); } + + virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) {} + + virtual void soft_body_set_space(RID p_body, RID p_space) {} + virtual RID soft_body_get_space(RID p_body) const { return RID(); } + + virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {} + virtual uint32_t soft_body_get_collision_layer(RID p_body) const { return 0; } + + virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {} + virtual uint32_t soft_body_get_collision_mask(RID p_body) const { return 0; } + + virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) {} + virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) {} + virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {} + + virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {} + virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const { return Variant(); } + + virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) {} + virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const { return Vector3(); } + + virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) {} + virtual bool soft_body_is_ray_pickable(RID p_body) const { return false; } + + virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {} + virtual int soft_body_get_simulation_precision(RID p_body) { return 0; } + + virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) {} + virtual real_t soft_body_get_total_mass(RID p_body) { return 0.; } + + virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) {} + virtual real_t soft_body_get_linear_stiffness(RID p_body) { return 0.; } + + virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) {} + virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) { return 0.; } + + virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {} + virtual real_t soft_body_get_volume_stiffness(RID p_body) { return 0.; } + + virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {} + virtual real_t soft_body_get_pressure_coefficient(RID p_body) { return 0.; } + + virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {} + virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) { return 0.; } + + virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {} + virtual real_t soft_body_get_damping_coefficient(RID p_body) { return 0.; } + + virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {} + virtual real_t soft_body_get_drag_coefficient(RID p_body) { return 0.; } + + virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) {} + + virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {} + virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) { return Vector3(); } + + virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const { return Vector3(); } + + virtual void soft_body_remove_all_pinned_points(RID p_body) {} + virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {} + virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) { return 0; } + /* JOINT API */ virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B); diff --git a/servers/physics/space_sw.h b/servers/physics/space_sw.h index 2452d6a187..4d864e9a51 100644 --- a/servers/physics/space_sw.h +++ b/servers/physics/space_sw.h @@ -186,7 +186,7 @@ public: void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); } _FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.empty(); } _FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) { - if (contact_debug_count < contact_debug.size()) contact_debug[contact_debug_count++] = p_contact; + if (contact_debug_count < contact_debug.size()) contact_debug.write[contact_debug_count++] = p_contact; } _FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contact_debug; } _FORCE_INLINE_ int get_debug_contact_count() { return contact_debug_count; } diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h index 782adf3416..69184ad484 100644 --- a/servers/physics_2d/body_2d_sw.h +++ b/servers/physics_2d/body_2d_sw.h @@ -139,7 +139,7 @@ public: _FORCE_INLINE_ void add_area(Area2DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { - areas[index].refCount += 1; + areas.write[index].refCount += 1; } else { areas.ordered_insert(AreaCMP(p_area)); } @@ -148,7 +148,7 @@ public: _FORCE_INLINE_ void remove_area(Area2DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { - areas[index].refCount -= 1; + areas.write[index].refCount -= 1; if (areas[index].refCount < 1) areas.remove(index); } @@ -199,12 +199,20 @@ public: _FORCE_INLINE_ void set_biased_angular_velocity(real_t p_velocity) { biased_angular_velocity = p_velocity; } _FORCE_INLINE_ real_t get_biased_angular_velocity() const { return biased_angular_velocity; } + _FORCE_INLINE_ void apply_central_impulse(const Vector2 &p_impulse) { + linear_velocity += p_impulse * _inv_mass; + } + _FORCE_INLINE_ void apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse) { linear_velocity += p_impulse * _inv_mass; angular_velocity += _inv_inertia * p_offset.cross(p_impulse); } + _FORCE_INLINE_ void apply_torque_impulse(real_t p_torque) { + angular_velocity += _inv_inertia * p_torque; + } + _FORCE_INLINE_ void apply_bias_impulse(const Vector2 &p_pos, const Vector2 &p_j) { biased_linear_velocity += p_j * _inv_mass; @@ -235,12 +243,20 @@ public: void set_applied_torque(real_t p_torque) { applied_torque = p_torque; } real_t get_applied_torque() const { return applied_torque; } - _FORCE_INLINE_ void add_force(const Vector2 &p_force, const Vector2 &p_offset) { + _FORCE_INLINE_ void add_central_force(const Vector2 &p_force) { + applied_force += p_force; + } + + _FORCE_INLINE_ void add_force(const Vector2 &p_offset, const Vector2 &p_force) { applied_force += p_force; applied_torque += p_offset.cross(p_force); } + _FORCE_INLINE_ void add_torque(real_t p_torque) { + applied_torque += p_torque; + } + _FORCE_INLINE_ void set_continuous_collision_detection_mode(Physics2DServer::CCDMode p_mode) { continuous_cd_mode = p_mode; } _FORCE_INLINE_ Physics2DServer::CCDMode get_continuous_collision_detection_mode() const { return continuous_cd_mode; } @@ -287,7 +303,7 @@ void Body2DSW::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_no if (c_max == 0) return; - Contact *c = &contacts[0]; + Contact *c = contacts.ptrw(); int idx = -1; @@ -349,6 +365,13 @@ public: virtual void set_transform(const Transform2D &p_transform) { body->set_state(Physics2DServer::BODY_STATE_TRANSFORM, p_transform); } virtual Transform2D get_transform() const { return body->get_transform(); } + virtual void add_central_force(const Vector2 &p_force) { body->add_central_force(p_force); } + virtual void add_force(const Vector2 &p_offset, const Vector2 &p_force) { body->add_force(p_offset, p_force); } + virtual void add_torque(real_t p_torque) { body->add_torque(p_torque); } + virtual void apply_central_impulse(const Vector2 &p_impulse) { body->apply_central_impulse(p_impulse); } + virtual void apply_impulse(const Vector2 &p_offset, const Vector2 &p_force) { body->apply_impulse(p_offset, p_force); } + virtual void apply_torque_impulse(real_t p_torque) { body->apply_torque_impulse(p_torque); } + virtual void set_sleep_state(bool p_enable) { body->set_active(!p_enable); } virtual bool is_sleeping() const { return !body->is_active(); } diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp index 61c0e0063f..2633edf7bb 100644 --- a/servers/physics_2d/body_pair_2d_sw.cpp +++ b/servers/physics_2d/body_pair_2d_sw.cpp @@ -219,6 +219,14 @@ bool BodyPair2DSW::_test_ccd(real_t p_step, Body2DSW *p_A, int p_shape_A, const return true; } +real_t combine_bounce(Body2DSW *A, Body2DSW *B) { + return CLAMP(A->get_bounce() + B->get_bounce(), 0, 1); +} + +real_t combine_friction(Body2DSW *A, Body2DSW *B) { + return ABS(MIN(A->get_friction(), B->get_friction())); +} + bool BodyPair2DSW::setup(real_t p_step) { //cannot collide @@ -432,7 +440,7 @@ bool BodyPair2DSW::setup(real_t p_step) { #endif - c.bounce = MAX(A->get_bounce(), B->get_bounce()); + c.bounce = combine_bounce(A, B); if (c.bounce) { Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x); @@ -488,7 +496,7 @@ void BodyPair2DSW::solve(real_t p_step) { real_t jnOld = c.acc_normal_impulse; c.acc_normal_impulse = MAX(jnOld + jn, 0.0f); - real_t friction = A->get_friction() * B->get_friction(); + real_t friction = combine_friction(A, B); real_t jtMax = friction * c.acc_normal_impulse; real_t jt = -vt * c.mass_tangent; diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp index 23084a4241..4dd5b2040f 100644 --- a/servers/physics_2d/collision_object_2d_sw.cpp +++ b/servers/physics_2d/collision_object_2d_sw.cpp @@ -50,7 +50,7 @@ void CollisionObject2DSW::set_shape(int p_index, Shape2DSW *p_shape) { ERR_FAIL_INDEX(p_index, shapes.size()); shapes[p_index].shape->remove_owner(this); - shapes[p_index].shape = p_shape; + shapes.write[p_index].shape = p_shape; p_shape->add_owner(this); _update_shapes(); @@ -60,15 +60,15 @@ void CollisionObject2DSW::set_shape(int p_index, Shape2DSW *p_shape) { void CollisionObject2DSW::set_shape_metadata(int p_index, const Variant &p_metadata) { ERR_FAIL_INDEX(p_index, shapes.size()); - shapes[p_index].metadata = p_metadata; + shapes.write[p_index].metadata = p_metadata; } void CollisionObject2DSW::set_shape_transform(int p_index, const Transform2D &p_transform) { ERR_FAIL_INDEX(p_index, shapes.size()); - shapes[p_index].xform = p_transform; - shapes[p_index].xform_inv = p_transform.affine_inverse(); + shapes.write[p_index].xform = p_transform; + shapes.write[p_index].xform_inv = p_transform.affine_inverse(); _update_shapes(); _shapes_changed(); } @@ -76,7 +76,7 @@ void CollisionObject2DSW::set_shape_transform(int p_index, const Transform2D &p_ void CollisionObject2DSW::set_shape_as_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, shapes.size()); - CollisionObject2DSW::Shape &shape = shapes[p_idx]; + CollisionObject2DSW::Shape &shape = shapes.write[p_idx]; if (shape.disabled == p_disabled) return; @@ -116,7 +116,7 @@ void CollisionObject2DSW::remove_shape(int p_index) { continue; //should never get here with a null owner space->get_broadphase()->remove(shapes[i].bpid); - shapes[i].bpid = 0; + shapes.write[i].bpid = 0; } shapes[p_index].shape->remove_owner(this); shapes.remove(p_index); @@ -133,7 +133,7 @@ void CollisionObject2DSW::_set_static(bool p_static) { if (!space) return; for (int i = 0; i < get_shape_count(); i++) { - Shape &s = shapes[i]; + const Shape &s = shapes[i]; if (s.bpid > 0) { space->get_broadphase()->set_static(s.bpid, _static); } @@ -144,7 +144,7 @@ void CollisionObject2DSW::_unregister_shapes() { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid > 0) { space->get_broadphase()->remove(s.bpid); s.bpid = 0; @@ -159,7 +159,7 @@ void CollisionObject2DSW::_update_shapes() { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.disabled) continue; @@ -187,7 +187,7 @@ void CollisionObject2DSW::_update_shapes_with_motion(const Vector2 &p_motion) { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.disabled) continue; @@ -215,7 +215,7 @@ void CollisionObject2DSW::_set_space(Space2DSW *p_space) { for (int i = 0; i < shapes.size(); i++) { - Shape &s = shapes[i]; + Shape &s = shapes.write[i]; if (s.bpid) { space->get_broadphase()->remove(s.bpid); s.bpid = 0; diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h index ab3e219ac0..393c4a6ed7 100644 --- a/servers/physics_2d/collision_object_2d_sw.h +++ b/servers/physics_2d/collision_object_2d_sw.h @@ -144,7 +144,7 @@ public: _FORCE_INLINE_ void set_shape_as_one_way_collision(int p_idx, bool p_one_way_collision) { ERR_FAIL_INDEX(p_idx, shapes.size()); - shapes[p_idx].one_way_collision = p_one_way_collision; + shapes.write[p_idx].one_way_collision = p_one_way_collision; } _FORCE_INLINE_ bool is_shape_set_as_one_way_collision(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, shapes.size(), false); diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index a473e0beb2..15e80bcd5e 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -838,6 +838,21 @@ real_t Physics2DServerSW::body_get_applied_torque(RID p_body) const { return body->get_applied_torque(); }; +void Physics2DServerSW::body_apply_central_impulse(RID p_body, const Vector2 &p_impulse) { + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_central_impulse(p_impulse); + body->wakeup(); +} + +void Physics2DServerSW::body_apply_torque_impulse(RID p_body, real_t p_torque) { + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->apply_torque_impulse(p_torque); +} + void Physics2DServerSW::body_apply_impulse(RID p_body, const Vector2 &p_pos, const Vector2 &p_impulse) { Body2DSW *body = body_owner.get(p_body); @@ -847,12 +862,28 @@ void Physics2DServerSW::body_apply_impulse(RID p_body, const Vector2 &p_pos, con body->wakeup(); }; +void Physics2DServerSW::body_add_central_force(RID p_body, const Vector2 &p_force) { + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->add_central_force(p_force); + body->wakeup(); +}; + void Physics2DServerSW::body_add_force(RID p_body, const Vector2 &p_offset, const Vector2 &p_force) { Body2DSW *body = body_owner.get(p_body); ERR_FAIL_COND(!body); - body->add_force(p_force, p_offset); + body->add_force(p_offset, p_force); + body->wakeup(); +}; + +void Physics2DServerSW::body_add_torque(RID p_body, real_t p_torque) { + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND(!body); + + body->add_torque(p_torque); body->wakeup(); }; diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h index e5961b9011..d4fc44b1d7 100644 --- a/servers/physics_2d/physics_2d_server_sw.h +++ b/servers/physics_2d/physics_2d_server_sw.h @@ -209,8 +209,12 @@ public: virtual void body_set_applied_torque(RID p_body, real_t p_torque); virtual real_t body_get_applied_torque(RID p_body) const; + virtual void body_add_central_force(RID p_body, const Vector2 &p_force); virtual void body_add_force(RID p_body, const Vector2 &p_offset, const Vector2 &p_force); + virtual void body_add_torque(RID p_body, real_t p_torque); + virtual void body_apply_central_impulse(RID p_body, const Vector2 &p_impulse); + virtual void body_apply_torque_impulse(RID p_body, real_t p_torque); virtual void body_apply_impulse(RID p_body, const Vector2 &p_pos, const Vector2 &p_impulse); virtual void body_set_axis_velocity(RID p_body, const Vector2 &p_axis_velocity); diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h index 3119b930d7..6b34fb9739 100644 --- a/servers/physics_2d/physics_2d_server_wrap_mt.h +++ b/servers/physics_2d/physics_2d_server_wrap_mt.h @@ -220,7 +220,11 @@ public: FUNC2(body_set_applied_torque, RID, real_t); FUNC1RC(real_t, body_get_applied_torque, RID); + FUNC2(body_add_central_force, RID, const Vector2 &); FUNC3(body_add_force, RID, const Vector2 &, const Vector2 &); + FUNC2(body_add_torque, RID, real_t); + FUNC2(body_apply_central_impulse, RID, const Vector2 &); + FUNC2(body_apply_torque_impulse, RID, real_t); FUNC3(body_apply_impulse, RID, const Vector2 &, const Vector2 &); FUNC2(body_set_axis_velocity, RID, const Vector2 &); diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp index 2b0eab5999..dc8ec23e69 100644 --- a/servers/physics_2d/shape_2d_sw.cpp +++ b/servers/physics_2d/shape_2d_sw.cpp @@ -891,8 +891,8 @@ int ConcavePolygonShape2DSW::_generate_bvh(BVH *p_bvh, int p_len, int p_depth) { int l = _generate_bvh(p_bvh, median, p_depth + 1); int r = _generate_bvh(&p_bvh[median], p_len - median, p_depth + 1); - bvh[node_idx].left = l; - bvh[node_idx].right = r; + bvh.write[node_idx].left = l; + bvh.write[node_idx].right = r; return node_idx; } @@ -953,20 +953,20 @@ void ConcavePolygonShape2DSW::set_data(const Variant &p_data) { for (Map<Point2, int>::Element *E = pointmap.front(); E; E = E->next()) { aabb.expand_to(E->key()); - points[E->get()] = E->key(); + points.write[E->get()] = E->key(); } Vector<BVH> main_vbh; main_vbh.resize(segments.size()); for (int i = 0; i < main_vbh.size(); i++) { - main_vbh[i].aabb.position = points[segments[i].points[0]]; - main_vbh[i].aabb.expand_to(points[segments[i].points[1]]); - main_vbh[i].left = -1; - main_vbh[i].right = i; + main_vbh.write[i].aabb.position = points[segments[i].points[0]]; + main_vbh.write[i].aabb.expand_to(points[segments[i].points[1]]); + main_vbh.write[i].left = -1; + main_vbh.write[i].right = i; } - _generate_bvh(&main_vbh[0], main_vbh.size(), 1); + _generate_bvh(main_vbh.ptrw(), main_vbh.size(), 1); } else { //dictionary with arrays diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 959e15e12d..1247317b03 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -188,7 +188,7 @@ public: void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); } _FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.empty(); } _FORCE_INLINE_ void add_debug_contact(const Vector2 &p_contact) { - if (contact_debug_count < contact_debug.size()) contact_debug[contact_debug_count++] = p_contact; + if (contact_debug_count < contact_debug.size()) contact_debug.write[contact_debug_count++] = p_contact; } _FORCE_INLINE_ Vector<Vector2> get_debug_contacts() { return contact_debug; } _FORCE_INLINE_ int get_debug_contact_count() { return contact_debug_count; } diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp index cb7669ec24..37c4bc83ad 100644 --- a/servers/physics_2d_server.cpp +++ b/servers/physics_2d_server.cpp @@ -91,6 +91,13 @@ void Physics2DDirectBodyState::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "transform"), &Physics2DDirectBodyState::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &Physics2DDirectBodyState::get_transform); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &Physics2DDirectBodyState::add_central_force); + ClassDB::bind_method(D_METHOD("add_force", "offset", "force"), &Physics2DDirectBodyState::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &Physics2DDirectBodyState::add_torque); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &Physics2DDirectBodyState::apply_central_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &Physics2DDirectBodyState::apply_torque_impulse); + ClassDB::bind_method(D_METHOD("apply_impulse", "offset", "impulse"), &Physics2DDirectBodyState::apply_impulse); + ClassDB::bind_method(D_METHOD("set_sleep_state", "enabled"), &Physics2DDirectBodyState::set_sleep_state); ClassDB::bind_method(D_METHOD("is_sleeping"), &Physics2DDirectBodyState::is_sleeping); @@ -191,7 +198,7 @@ Vector<RID> Physics2DShapeQueryParameters::get_exclude() const { ret.resize(exclude.size()); int idx = 0; for (Set<RID>::Element *E = exclude.front(); E; E = E->next()) { - ret[idx] = E->get(); + ret.write[idx] = E->get(); } return ret; } @@ -585,8 +592,12 @@ void Physics2DServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_state", "body", "state", "value"), &Physics2DServer::body_set_state); ClassDB::bind_method(D_METHOD("body_get_state", "body", "state"), &Physics2DServer::body_get_state); + ClassDB::bind_method(D_METHOD("body_apply_central_impulse", "body", "impulse"), &Physics2DServer::body_apply_central_impulse); + ClassDB::bind_method(D_METHOD("body_apply_torque_impulse", "body", "impulse"), &Physics2DServer::body_apply_torque_impulse); ClassDB::bind_method(D_METHOD("body_apply_impulse", "body", "position", "impulse"), &Physics2DServer::body_apply_impulse); + ClassDB::bind_method(D_METHOD("body_add_central_force", "body", "force"), &Physics2DServer::body_add_central_force); ClassDB::bind_method(D_METHOD("body_add_force", "body", "offset", "force"), &Physics2DServer::body_add_force); + ClassDB::bind_method(D_METHOD("body_add_torque", "body", "torque"), &Physics2DServer::body_add_torque); ClassDB::bind_method(D_METHOD("body_set_axis_velocity", "body", "axis_velocity"), &Physics2DServer::body_set_axis_velocity); ClassDB::bind_method(D_METHOD("body_add_collision_exception", "body", "excepted_body"), &Physics2DServer::body_add_collision_exception); diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index 1d04fbc5c6..f42d9868f0 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -61,6 +61,13 @@ public: virtual void set_transform(const Transform2D &p_transform) = 0; virtual Transform2D get_transform() const = 0; + virtual void add_central_force(const Vector2 &p_force) = 0; + virtual void add_force(const Vector2 &p_offset, const Vector2 &p_force) = 0; + virtual void add_torque(real_t p_torque) = 0; + virtual void apply_central_impulse(const Vector2 &p_impulse) = 0; + virtual void apply_torque_impulse(real_t p_torque) = 0; + virtual void apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse) = 0; + virtual void set_sleep_state(bool p_enable) = 0; virtual bool is_sleeping() const = 0; @@ -435,8 +442,12 @@ public: virtual void body_set_applied_torque(RID p_body, float p_torque) = 0; virtual float body_get_applied_torque(RID p_body) const = 0; + virtual void body_add_central_force(RID p_body, const Vector2 &p_force) = 0; virtual void body_add_force(RID p_body, const Vector2 &p_offset, const Vector2 &p_force) = 0; + virtual void body_add_torque(RID p_body, float p_torque) = 0; + virtual void body_apply_central_impulse(RID p_body, const Vector2 &p_impulse) = 0; + virtual void body_apply_torque_impulse(RID p_body, float p_torque) = 0; virtual void body_apply_impulse(RID p_body, const Vector2 &p_offset, const Vector2 &p_impulse) = 0; virtual void body_set_axis_velocity(RID p_body, const Vector2 &p_axis_velocity) = 0; diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index b4bd4cb35f..cda3856ecc 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -95,6 +95,7 @@ void PhysicsDirectBodyState::_bind_methods() { ClassDB::bind_method(D_METHOD("add_central_force", "force"), &PhysicsDirectBodyState::add_central_force); ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &PhysicsDirectBodyState::add_force); ClassDB::bind_method(D_METHOD("add_torque", "torque"), &PhysicsDirectBodyState::add_torque); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "j"), &PhysicsDirectBodyState::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "j"), &PhysicsDirectBodyState::apply_impulse); ClassDB::bind_method(D_METHOD("apply_torque_impulse", "j"), &PhysicsDirectBodyState::apply_torque_impulse); @@ -105,6 +106,7 @@ void PhysicsDirectBodyState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_contact_local_position", "contact_idx"), &PhysicsDirectBodyState::get_contact_local_position); ClassDB::bind_method(D_METHOD("get_contact_local_normal", "contact_idx"), &PhysicsDirectBodyState::get_contact_local_normal); + ClassDB::bind_method(D_METHOD("get_contact_impulse", "contact_idx"), &PhysicsDirectBodyState::get_contact_impulse); ClassDB::bind_method(D_METHOD("get_contact_local_shape", "contact_idx"), &PhysicsDirectBodyState::get_contact_local_shape); ClassDB::bind_method(D_METHOD("get_contact_collider", "contact_idx"), &PhysicsDirectBodyState::get_contact_collider); ClassDB::bind_method(D_METHOD("get_contact_collider_position", "contact_idx"), &PhysicsDirectBodyState::get_contact_collider_position); @@ -191,7 +193,7 @@ Vector<RID> PhysicsShapeQueryParameters::get_exclude() const { ret.resize(exclude.size()); int idx = 0; for (Set<RID>::Element *E = exclude.front(); E; E = E->next()) { - ret[idx] = E->get(); + ret.write[idx] = E->get(); } return ret; } @@ -495,6 +497,11 @@ void PhysicsServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_state", "body", "state", "value"), &PhysicsServer::body_set_state); ClassDB::bind_method(D_METHOD("body_get_state", "body", "state"), &PhysicsServer::body_get_state); + ClassDB::bind_method(D_METHOD("body_add_central_force", "body", "force"), &PhysicsServer::body_add_central_force); + ClassDB::bind_method(D_METHOD("body_add_force", "body", "force", "position"), &PhysicsServer::body_add_force); + ClassDB::bind_method(D_METHOD("body_add_torque", "body", "torque"), &PhysicsServer::body_add_torque); + + ClassDB::bind_method(D_METHOD("body_apply_central_impulse", "body", "impulse"), &PhysicsServer::body_apply_central_impulse); ClassDB::bind_method(D_METHOD("body_apply_impulse", "body", "position", "impulse"), &PhysicsServer::body_apply_impulse); ClassDB::bind_method(D_METHOD("body_apply_torque_impulse", "body", "impulse"), &PhysicsServer::body_apply_torque_impulse); ClassDB::bind_method(D_METHOD("body_set_axis_velocity", "body", "axis_velocity"), &PhysicsServer::body_set_axis_velocity); diff --git a/servers/physics_server.h b/servers/physics_server.h index 8ecf17c0e6..948aec1a2d 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -66,6 +66,7 @@ public: virtual void add_central_force(const Vector3 &p_force) = 0; virtual void add_force(const Vector3 &p_force, const Vector3 &p_pos) = 0; virtual void add_torque(const Vector3 &p_torque) = 0; + virtual void apply_central_impulse(const Vector3 &p_j) = 0; virtual void apply_impulse(const Vector3 &p_pos, const Vector3 &p_j) = 0; virtual void apply_torque_impulse(const Vector3 &p_j) = 0; @@ -76,6 +77,7 @@ public: virtual Vector3 get_contact_local_position(int p_contact_idx) const = 0; virtual Vector3 get_contact_local_normal(int p_contact_idx) const = 0; + virtual float get_contact_impulse(int p_contact_idx) const = 0; virtual int get_contact_local_shape(int p_contact_idx) const = 0; virtual RID get_contact_collider(int p_contact_idx) const = 0; @@ -421,6 +423,11 @@ public: virtual void body_set_applied_torque(RID p_body, const Vector3 &p_torque) = 0; virtual Vector3 body_get_applied_torque(RID p_body) const = 0; + virtual void body_add_central_force(RID p_body, const Vector3 &p_force) = 0; + virtual void body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_pos) = 0; + virtual void body_add_torque(RID p_body, const Vector3 &p_torque) = 0; + + virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) = 0; virtual void body_apply_impulse(RID p_body, const Vector3 &p_pos, const Vector3 &p_impulse) = 0; virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) = 0; virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) = 0; @@ -477,6 +484,72 @@ public: virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL) = 0; + /* SOFT BODY */ + + virtual RID soft_body_create(bool p_init_sleeping = false) = 0; + + virtual void soft_body_update_visual_server(RID p_body, class SoftBodyVisualServerHandler *p_visual_server_handler) = 0; + + virtual void soft_body_set_space(RID p_body, RID p_space) = 0; + virtual RID soft_body_get_space(RID p_body) const = 0; + + virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) = 0; + + virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) = 0; + virtual uint32_t soft_body_get_collision_layer(RID p_body) const = 0; + + virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) = 0; + virtual uint32_t soft_body_get_collision_mask(RID p_body) const = 0; + + virtual void soft_body_add_collision_exception(RID p_body, RID p_body_b) = 0; + virtual void soft_body_remove_collision_exception(RID p_body, RID p_body_b) = 0; + virtual void soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) = 0; + + virtual void soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) = 0; + virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const = 0; + + virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) = 0; + virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const = 0; + + virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) = 0; + virtual bool soft_body_is_ray_pickable(RID p_body) const = 0; + + virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) = 0; + virtual int soft_body_get_simulation_precision(RID p_body) = 0; + + virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) = 0; + virtual real_t soft_body_get_total_mass(RID p_body) = 0; + + virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) = 0; + virtual real_t soft_body_get_linear_stiffness(RID p_body) = 0; + + virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) = 0; + virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) = 0; + + virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) = 0; + virtual real_t soft_body_get_volume_stiffness(RID p_body) = 0; + + virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) = 0; + virtual real_t soft_body_get_pressure_coefficient(RID p_body) = 0; + + virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) = 0; + virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) = 0; + + virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) = 0; + virtual real_t soft_body_get_damping_coefficient(RID p_body) = 0; + + virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) = 0; + virtual real_t soft_body_get_drag_coefficient(RID p_body) = 0; + + virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) = 0; + virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) = 0; + + virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const = 0; + + virtual void soft_body_remove_all_pinned_points(RID p_body) = 0; + virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) = 0; + virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) = 0; + /* JOINT API */ enum JointType { diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 1bad7e652b..4c764641e3 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -48,6 +48,7 @@ #include "audio/effects/audio_effect_panner.h" #include "audio/effects/audio_effect_phaser.h" #include "audio/effects/audio_effect_pitch_shift.h" +#include "audio/effects/audio_effect_record.h" #include "audio/effects/audio_effect_reverb.h" #include "audio/effects/audio_effect_stereo_enhance.h" #include "audio_server.h" @@ -72,7 +73,7 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag usage.vram = E->get().bytes; usage.id = E->get().texture; usage.type = "Texture"; - usage.format = itos(E->get().size.width) + "x" + itos(E->get().size.height) + " " + Image::get_format_name(E->get().format); + usage.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format); r_usage->push_back(usage); } } @@ -103,6 +104,7 @@ void register_server_types() { ClassDB::register_virtual_class<AudioStream>(); ClassDB::register_virtual_class<AudioStreamPlayback>(); + ClassDB::register_class<AudioStreamMicrophone>(); ClassDB::register_class<AudioStreamRandomPitch>(); ClassDB::register_virtual_class<AudioEffect>(); ClassDB::register_class<AudioEffectEQ>(); @@ -138,6 +140,7 @@ void register_server_types() { ClassDB::register_class<AudioEffectLimiter>(); ClassDB::register_class<AudioEffectPitchShift>(); ClassDB::register_class<AudioEffectPhaser>(); + ClassDB::register_class<AudioEffectRecord>(); } ClassDB::register_virtual_class<Physics2DDirectBodyState>(); diff --git a/servers/server_wrap_mt_common.h b/servers/server_wrap_mt_common.h index 611e25af2a..843773e5b1 100644 --- a/servers/server_wrap_mt_common.h +++ b/servers/server_wrap_mt_common.h @@ -197,9 +197,10 @@ } #define FUNC5RID(m_type, m_arg1, m_arg2, m_arg3, m_arg4, m_arg5) \ - int m_type##allocn() { \ - for (int i = 0; i < m_type##_pool_max_size; i++) { \ - m_type##_id_pool.push_back(server_name->m_type##_create()); \ + List<RID> m_type##_id_pool; \ + int m_type##allocn(m_arg1 p1, m_arg2 p2, m_arg3 p3, m_arg4 p4, m_arg5 p5) { \ + for (int i = 0; i < pool_max_size; i++) { \ + m_type##_id_pool.push_back(server_name->m_type##_create(p1, p2, p3, p4, p5)); \ } \ return 0; \ } \ diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 0b37d266e7..49dff0d557 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -104,10 +104,12 @@ public: VS::ShadowCastingSetting cast_shadows; + //fit in 32 bits bool mirror : 8; bool receive_shadows : 8; bool visible : 8; - bool baked_light : 8; //this flag is only to know if it actually did use baked light + bool baked_light : 4; //this flag is only to know if it actually did use baked light + bool redraw_if_visible : 4; float depth; //used for sorting @@ -131,6 +133,7 @@ public: depth_layer = 0; layer_mask = 1; baked_light = false; + redraw_if_visible = false; lightmap_capture = NULL; } }; @@ -173,17 +176,34 @@ public: /* TEXTURE API */ virtual RID texture_create() = 0; - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) = 0; - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) = 0; - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) = 0; - virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const = 0; + virtual void texture_allocate(RID p_texture, + int p_width, + int p_height, + int p_depth_3d, + Image::Format p_format, + VS::TextureType p_type, + uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) = 0; + + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_level = 0) = 0; + + virtual void texture_set_data_partial(RID p_texture, + const Ref<Image> &p_image, + int src_x, int src_y, + int src_w, int src_h, + int dst_x, int dst_y, + int p_dst_mip, + int p_level = 0) = 0; + + virtual Ref<Image> texture_get_data(RID p_texture, int p_level = 0) const = 0; virtual void texture_set_flags(RID p_texture, uint32_t p_flags) = 0; virtual uint32_t texture_get_flags(RID p_texture) const = 0; virtual Image::Format texture_get_format(RID p_texture) const = 0; + virtual VS::TextureType texture_get_type(RID p_texture) const = 0; virtual uint32_t texture_get_texid(RID p_texture) const = 0; virtual uint32_t texture_get_width(RID p_texture) const = 0; virtual uint32_t texture_get_height(RID p_texture) const = 0; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0; + virtual uint32_t texture_get_depth(RID p_texture) const = 0; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) = 0; virtual void texture_set_path(RID p_texture, const String &p_path) = 0; virtual String texture_get_path(RID p_texture) const = 0; @@ -277,6 +297,7 @@ public: virtual AABB mesh_get_custom_aabb(RID p_mesh) const = 0; virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton) const = 0; + virtual void mesh_clear(RID p_mesh) = 0; /* MULTIMESH API */ @@ -1074,7 +1095,7 @@ public: virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale) = 0; virtual void initialize() = 0; - virtual void begin_frame() = 0; + virtual void begin_frame(double frame_step) = 0; virtual void set_current_render_target(RID p_render_target) = 0; virtual void restore_render_target() = 0; virtual void clear_render_target(const Color &p_color) = 0; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index fd1eb77143..ca50d0d049 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -123,6 +123,12 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "TYPE_SAMPLER2D", "TYPE_ISAMPLER2D", "TYPE_USAMPLER2D", + "TYPE_SAMPLER2DARRAY", + "TYPE_ISAMPLER2DARRAY", + "TYPE_USAMPLER2DARRAY", + "TYPE_SAMPLER3D", + "TYPE_ISAMPLER3D", + "TYPE_USAMPLER3D", "TYPE_SAMPLERCUBE", "INTERPOLATION_FLAT", "INTERPOLATION_NO_PERSPECTIVE", @@ -257,6 +263,12 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_TYPE_SAMPLER2D, "sampler2D" }, { TK_TYPE_ISAMPLER2D, "isampler2D" }, { TK_TYPE_USAMPLER2D, "usampler2D" }, + { TK_TYPE_SAMPLER2DARRAY, "sampler2DArray" }, + { TK_TYPE_ISAMPLER2DARRAY, "isampler2DArray" }, + { TK_TYPE_USAMPLER2DARRAY, "usampler2DArray" }, + { TK_TYPE_SAMPLER3D, "sampler3D" }, + { TK_TYPE_ISAMPLER3D, "isampler3D" }, + { TK_TYPE_USAMPLER3D, "usampler3D" }, { TK_TYPE_SAMPLERCUBE, "samplerCube" }, { TK_INTERPOLATION_FLAT, "flat" }, { TK_INTERPOLATION_NO_PERSPECTIVE, "noperspective" }, @@ -516,13 +528,14 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { bool hexa_found = false; bool sign_found = false; bool minus_exponent_found = false; + bool float_suffix_found = false; String str; int i = 0; while (true) { if (GETCHAR(i) == '.') { - if (period_found || exponent_found) + if (period_found || exponent_found || hexa_found || float_suffix_found) return _make_token(TK_ERROR, "Invalid numeric constant"); period_found = true; } else if (GETCHAR(i) == 'x') { @@ -530,11 +543,16 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_ERROR, "Invalid numeric constant"); hexa_found = true; } else if (GETCHAR(i) == 'e') { - if (hexa_found || exponent_found) + if (hexa_found || exponent_found || float_suffix_found) return _make_token(TK_ERROR, "Invalid numeric constant"); exponent_found = true; + } else if (GETCHAR(i) == 'f') { + if (hexa_found || exponent_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); + float_suffix_found = true; } else if (_is_number(GETCHAR(i))) { - //all ok + if (float_suffix_found) + return _make_token(TK_ERROR, "Invalid numeric constant"); } else if (hexa_found && _is_hex(GETCHAR(i))) { } else if ((GETCHAR(i) == '-' || GETCHAR(i) == '+') && exponent_found) { @@ -550,21 +568,60 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { i++; } - if (!_is_number(str[str.length() - 1])) - return _make_token(TK_ERROR, "Invalid numeric constant"); + CharType last_char = str[str.length() - 1]; + + if (hexa_found) { + //hex integers eg."0xFF" or "0x12AB", etc - NOT supported yet + return _make_token(TK_ERROR, "Invalid (hexadecimal) numeric constant - Not supported"); + } else if (period_found || float_suffix_found) { + //floats + if (period_found) { + if (float_suffix_found) { + //checks for eg "1.f" or "1.99f" notations + if (last_char != 'f') { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } else { + //checks for eg. "1." or "1.99" notations + if (last_char != '.' && !_is_number(last_char)) { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } + } else if (float_suffix_found) { + // if no period found the float suffix must be the last character, like in "2f" for "2.0" + if (last_char != 'f') { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } + + if (float_suffix_found) { + //strip the suffix + str = str.left(str.length() - 1); + //compensate reading cursor position + char_idx += 1; + } + + if (!str.is_valid_float()) { + return _make_token(TK_ERROR, "Invalid (float) numeric constant"); + } + } else { + //integers + if (!_is_number(last_char)) { + return _make_token(TK_ERROR, "Invalid (integer) numeric constant"); + } + if (!str.is_valid_integer()) { + return _make_token(TK_ERROR, "Invalid numeric constant"); + } + } char_idx += str.length(); Token tk; - if (period_found || minus_exponent_found) + if (period_found || minus_exponent_found || float_suffix_found) tk.type = TK_REAL_CONSTANT; else tk.type = TK_INT_CONSTANT; - if (!str.is_valid_float()) { - return _make_token(TK_ERROR, "Invalid numeric constant"); - } - - tk.constant = str.to_double(); + tk.constant = str.to_double(); //wont work with hex tk.line = tk_line; return tk; @@ -612,6 +669,8 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { } ERR_PRINT("BUG"); return Token(); + +#undef GETCHAR } String ShaderLanguage::token_debug(const String &p_code) { @@ -658,6 +717,12 @@ bool ShaderLanguage::is_token_datatype(TokenType p_type) { p_type == TK_TYPE_SAMPLER2D || p_type == TK_TYPE_ISAMPLER2D || p_type == TK_TYPE_USAMPLER2D || + p_type == TK_TYPE_SAMPLER2DARRAY || + p_type == TK_TYPE_ISAMPLER2DARRAY || + p_type == TK_TYPE_USAMPLER2DARRAY || + p_type == TK_TYPE_SAMPLER3D || + p_type == TK_TYPE_ISAMPLER3D || + p_type == TK_TYPE_USAMPLER3D || p_type == TK_TYPE_SAMPLERCUBE); } @@ -729,6 +794,12 @@ String ShaderLanguage::get_datatype_name(DataType p_type) { case TYPE_SAMPLER2D: return "sampler2D"; case TYPE_ISAMPLER2D: return "isampler2D"; case TYPE_USAMPLER2D: return "usampler2D"; + case TYPE_SAMPLER2DARRAY: return "sampler2DArray"; + case TYPE_ISAMPLER2DARRAY: return "isampler2DArray"; + case TYPE_USAMPLER2DARRAY: return "usampler2DArray"; + case TYPE_SAMPLER3D: return "sampler3D"; + case TYPE_ISAMPLER3D: return "isampler3D"; + case TYPE_USAMPLER3D: return "usampler3D"; case TYPE_SAMPLERCUBE: return "samplerCube"; } @@ -1376,6 +1447,15 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "bvec4", TYPE_BVEC4, { TYPE_UVEC4, TYPE_VOID } }, { "bvec4", TYPE_BVEC4, { TYPE_VEC4, TYPE_VOID } }, + //conversion between matrixes + + { "mat2", TYPE_MAT2, { TYPE_MAT3, TYPE_VOID } }, + { "mat2", TYPE_MAT2, { TYPE_MAT4, TYPE_VOID } }, + { "mat3", TYPE_MAT3, { TYPE_MAT2, TYPE_VOID } }, + { "mat3", TYPE_MAT3, { TYPE_MAT4, TYPE_VOID } }, + { "mat4", TYPE_MAT4, { TYPE_MAT2, TYPE_VOID } }, + { "mat4", TYPE_MAT4, { TYPE_MAT3, TYPE_VOID } }, + //builtins - trigonometry { "radians", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, @@ -1791,6 +1871,12 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "textureSize", TYPE_IVEC2, { TYPE_SAMPLER2D, TYPE_INT, TYPE_VOID } }, { "textureSize", TYPE_IVEC2, { TYPE_ISAMPLER2D, TYPE_INT, TYPE_VOID } }, { "textureSize", TYPE_IVEC2, { TYPE_USAMPLER2D, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER2DARRAY, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER2DARRAY, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER2DARRAY, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_SAMPLER3D, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_ISAMPLER3D, TYPE_INT, TYPE_VOID } }, + { "textureSize", TYPE_IVEC3, { TYPE_USAMPLER3D, TYPE_INT, TYPE_VOID } }, { "textureSize", TYPE_IVEC2, { TYPE_SAMPLERCUBE, TYPE_INT, TYPE_VOID } }, { "texture", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID } }, @@ -1802,6 +1888,24 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID } }, { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VOID } }, + { "texture", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID } }, { "texture", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, @@ -1820,15 +1924,38 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_VOID } }, + { "textureProj", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, + + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_VOID } }, + { "textureProj", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, + + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_VOID } }, + { "textureProj", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, + { "textureLod", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "textureLod", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_IVEC2, TYPE_INT, TYPE_VOID } }, + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + + { "texelFetch", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + { "texelFetch", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + { "texelFetch", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_IVEC3, TYPE_INT, TYPE_VOID } }, + { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC3, TYPE_FLOAT, TYPE_VOID } }, { "textureProjLod", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, @@ -1841,6 +1968,12 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VEC2, TYPE_VEC2, TYPE_VOID } }, + { "textureGrad", TYPE_VEC4, { TYPE_SAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "textureGrad", TYPE_IVEC4, { TYPE_ISAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, + { "textureGrad", TYPE_UVEC4, { TYPE_USAMPLER3D, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } }, @@ -2128,7 +2261,16 @@ bool ShaderLanguage::is_scalar_type(DataType p_type) { bool ShaderLanguage::is_sampler_type(DataType p_type) { - return p_type == TYPE_SAMPLER2D || p_type == TYPE_ISAMPLER2D || p_type == TYPE_USAMPLER2D || p_type == TYPE_SAMPLERCUBE; + return p_type == TYPE_SAMPLER2D || + p_type == TYPE_ISAMPLER2D || + p_type == TYPE_USAMPLER2D || + p_type == TYPE_SAMPLER2DARRAY || + p_type == TYPE_ISAMPLER2DARRAY || + p_type == TYPE_USAMPLER2DARRAY || + p_type == TYPE_SAMPLER3D || + p_type == TYPE_ISAMPLER3D || + p_type == TYPE_USAMPLER3D || + p_type == TYPE_SAMPLERCUBE; } void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { @@ -2295,24 +2437,54 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { return false; } -bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types) { +bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) { if (p_node->type == Node::TYPE_OPERATOR) { OperatorNode *op = static_cast<OperatorNode *>(p_node); + if (op->op == OP_INDEX) { - return _validate_assign(op->arguments[0], p_builtin_types); + return _validate_assign(op->arguments[0], p_builtin_types, r_message); + + } else if (_is_operator_assign(op->op)) { + //chained assignment + return _validate_assign(op->arguments[1], p_builtin_types, r_message); + + } else if (op->op == OP_CALL) { + if (r_message) + *r_message = RTR("Assignment to function."); + return false; } - } - if (p_node->type == Node::TYPE_VARIABLE) { + } else if (p_node->type == Node::TYPE_MEMBER) { + + MemberNode *member = static_cast<MemberNode *>(p_node); + return _validate_assign(member->owner, p_builtin_types, r_message); + + } else if (p_node->type == Node::TYPE_VARIABLE) { VariableNode *var = static_cast<VariableNode *>(p_node); - if (p_builtin_types.has(var->name) && p_builtin_types[var->name].constant) { - return false; //ops not valid + + if (shader->uniforms.has(var->name)) { + if (r_message) + *r_message = RTR("Assignment to uniform."); + return false; + } + + if (shader->varyings.has(var->name) && current_function != String("vertex")) { + if (r_message) + *r_message = RTR("Varyings can only be assigned in vertex function."); + return false; + } + + if (!(p_builtin_types.has(var->name) && p_builtin_types[var->name].constant)) { + return true; } } - return true; + + if (r_message) + *r_message = "Assignment to constant expression."; + return false; } ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { @@ -2460,7 +2632,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //add to current function as dependency for (int j = 0; j < shader->functions.size(); j++) { if (shader->functions[j].name == current_function) { - shader->functions[j].uses_function.insert(name); + shader->functions.write[j].uses_function.insert(name); break; } } @@ -3010,8 +3182,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } op->arguments.push_back(expression[i + 1].node); - expression[i].is_op = false; - expression[i].node = op; + expression.write[i].is_op = false; + expression.write[i].node = op; if (!_validate_operator(op, &op->return_cache)) { @@ -3045,8 +3217,8 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->arguments.push_back(expression[next_op + 1].node); op->arguments.push_back(expression[next_op + 3].node); - expression[next_op - 1].is_op = false; - expression[next_op - 1].node = op; + expression.write[next_op - 1].is_op = false; + expression.write[next_op - 1].node = op; if (!_validate_operator(op, &op->return_cache)) { String at; @@ -3079,10 +3251,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons ERR_FAIL_V(NULL); } - if (_is_operator_assign(op->op) && !_validate_assign(expression[next_op - 1].node, p_builtin_types)) { + if (_is_operator_assign(op->op)) { - _set_error("Assignment to constant expression."); - return NULL; + String assign_message; + if (!_validate_assign(expression[next_op - 1].node, p_builtin_types, &assign_message)) { + + _set_error(assign_message); + return NULL; + } } if (expression[next_op + 1].is_op) { @@ -3096,7 +3272,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->arguments.push_back(expression[next_op - 1].node); //expression goes as left op->arguments.push_back(expression[next_op + 1].node); //next expression goes as right - expression[next_op - 1].node = op; + expression.write[next_op - 1].node = op; //replace all 3 nodes by this operator and make it an expression @@ -3138,7 +3314,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha for (int i = 1; i < op->arguments.size(); i++) { - op->arguments[i] = _reduce_expression(p_block, op->arguments[i]); + op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]); if (op->arguments[i]->type == Node::TYPE_CONSTANT) { ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[i]); @@ -3178,7 +3354,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha return cn; } else if (op->op == OP_NEGATE) { - op->arguments[0] = _reduce_expression(p_block, op->arguments[0]); + op->arguments.write[0] = _reduce_expression(p_block, op->arguments[0]); if (op->arguments[0]->type == Node::TYPE_CONSTANT) { ConstantNode *cn = static_cast<ConstantNode *>(op->arguments[0]); @@ -4064,13 +4240,58 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return OK; } +// skips over whitespace and /* */ and // comments +static int _get_first_ident_pos(const String &p_code) { + + int idx = 0; + +#define GETCHAR(m_idx) (((idx + m_idx) < p_code.length()) ? p_code[idx + m_idx] : CharType(0)) + + while (true) { + if (GETCHAR(0) == '/' && GETCHAR(1) == '/') { + idx += 2; + while (true) { + if (GETCHAR(0) == 0) return 0; + if (GETCHAR(0) == '\n') { + idx++; + break; // loop + } + idx++; + } + } else if (GETCHAR(0) == '/' && GETCHAR(1) == '*') { + idx += 2; + while (true) { + if (GETCHAR(0) == 0) return 0; + if (GETCHAR(0) == '*' && GETCHAR(1) == '/') { + idx += 2; + break; // loop + } + idx++; + } + } else { + switch (GETCHAR(0)) { + case ' ': + case '\t': + case '\r': + case '\n': { + idx++; + } break; // switch + default: + return idx; + } + } + } + +#undef GETCHAR +} + String ShaderLanguage::get_shader_type(const String &p_code) { bool reading_type = false; String cur_identifier; - for (int i = 0; i < p_code.length(); i++) { + for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) { if (p_code[i] == ';') { break; diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index 9b84c5f195..d68f233b2f 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -72,6 +72,12 @@ public: TK_TYPE_SAMPLER2D, TK_TYPE_ISAMPLER2D, TK_TYPE_USAMPLER2D, + TK_TYPE_SAMPLER2DARRAY, + TK_TYPE_ISAMPLER2DARRAY, + TK_TYPE_USAMPLER2DARRAY, + TK_TYPE_SAMPLER3D, + TK_TYPE_ISAMPLER3D, + TK_TYPE_USAMPLER3D, TK_TYPE_SAMPLERCUBE, TK_INTERPOLATION_FLAT, TK_INTERPOLATION_NO_PERSPECTIVE, @@ -186,6 +192,12 @@ public: TYPE_SAMPLER2D, TYPE_ISAMPLER2D, TYPE_USAMPLER2D, + TYPE_SAMPLER2DARRAY, + TYPE_ISAMPLER2DARRAY, + TYPE_USAMPLER2DARRAY, + TYPE_SAMPLER3D, + TYPE_ISAMPLER3D, + TYPE_USAMPLER3D, TYPE_SAMPLERCUBE, }; @@ -617,7 +629,7 @@ private: bool _find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL); bool _is_operator_assign(Operator p_op) const; - bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types); + bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = NULL); bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = NULL); diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 6439ba8509..0b4bbffddf 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -220,7 +220,7 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr for (int i = 0; i < l; i++) { - Canvas::ChildItem &ci = p_canvas->child_items[i]; + const Canvas::ChildItem &ci = p_canvas->child_items[i]; _render_canvas_item_tree(ci.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights); //mirroring (useful for scrolling backgrounds) @@ -263,7 +263,7 @@ void VisualServerCanvas::canvas_set_item_mirroring(RID p_canvas, RID p_item, con int idx = canvas->find_item(canvas_item); ERR_FAIL_COND(idx == -1); - canvas->child_items[idx].mirror = p_mirroring; + canvas->child_items.write[idx].mirror = p_mirroring; } void VisualServerCanvas::canvas_set_modulate(RID p_canvas, const Color &p_color) { @@ -468,21 +468,21 @@ void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vector<Point Vector2 tangent = ((t + prev_t).normalized()) * p_width * 0.5; if (p_antialiased) { - pline->lines[i] = p_points[i] + tangent; - pline->lines[p_points.size() * 2 - i - 1] = p_points[i] - tangent; + pline->lines.write[i] = p_points[i] + tangent; + pline->lines.write[p_points.size() * 2 - i - 1] = p_points[i] - tangent; if (pline->line_colors.size() > 1) { - pline->line_colors[i] = p_colors[i]; - pline->line_colors[p_points.size() * 2 - i - 1] = p_colors[i]; + pline->line_colors.write[i] = p_colors[i]; + pline->line_colors.write[p_points.size() * 2 - i - 1] = p_colors[i]; } } - pline->triangles[i * 2 + 0] = p_points[i] + tangent; - pline->triangles[i * 2 + 1] = p_points[i] - tangent; + pline->triangles.write[i * 2 + 0] = p_points[i] + tangent; + pline->triangles.write[i * 2 + 1] = p_points[i] - tangent; if (pline->triangle_colors.size() > 1) { - pline->triangle_colors[i * 2 + 0] = p_colors[i]; - pline->triangle_colors[i * 2 + 1] = p_colors[i]; + pline->triangle_colors.write[i * 2 + 0] = p_colors[i]; + pline->triangle_colors.write[i * 2 + 1] = p_colors[i]; } prev_t = t; @@ -669,7 +669,7 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2 int color_size = p_colors.size(); int uv_size = p_uvs.size(); ERR_FAIL_COND(color_size != 0 && color_size != 1 && color_size != pointcount); - ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount || !p_texture.is_valid())); + ERR_FAIL_COND(uv_size != 0 && (uv_size != pointcount)); #endif Vector<int> indices = Geometry::triangulate_polygon(p_points); diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 5f207b1d3f..c7d33ec43c 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -93,14 +93,14 @@ void VisualServerRaster::request_frame_drawn_callback(Object *p_where, const Str frame_drawn_callbacks.push_back(fdc); } -void VisualServerRaster::draw(bool p_swap_buffers) { +void VisualServerRaster::draw(bool p_swap_buffers, double frame_step) { //needs to be done before changes is reset to 0, to not force the editor to redraw VS::get_singleton()->emit_signal("frame_pre_draw"); changes = 0; - VSG::rasterizer->begin_frame(); + VSG::rasterizer->begin_frame(frame_step); VSG::scene->update_dirty_instances(); //update scene stuff @@ -161,6 +161,7 @@ void VisualServerRaster::set_boot_image(const Ref<Image> &p_image, const Color & VSG::rasterizer->set_boot_image(p_image, p_color, p_scale); } void VisualServerRaster::set_default_clear_color(const Color &p_color) { + VSG::viewport->set_default_clear_color(p_color); } bool VisualServerRaster::has_feature(Features p_feature) const { diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index ec0d02ed2a..a00b364565 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -148,17 +148,19 @@ public: /* TEXTURE API */ BIND0R(RID, texture_create) - BIND5(texture_allocate, RID, int, int, Image::Format, uint32_t) - BIND3(texture_set_data, RID, const Ref<Image> &, CubeMapSide) - BIND10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, CubeMapSide) - BIND2RC(Ref<Image>, texture_get_data, RID, CubeMapSide) + BIND7(texture_allocate, RID, int, int, int, Image::Format, TextureType, uint32_t) + BIND3(texture_set_data, RID, const Ref<Image> &, int) + BIND10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, int) + BIND2RC(Ref<Image>, texture_get_data, RID, int) BIND2(texture_set_flags, RID, uint32_t) BIND1RC(uint32_t, texture_get_flags, RID) BIND1RC(Image::Format, texture_get_format, RID) + BIND1RC(TextureType, texture_get_type, RID) BIND1RC(uint32_t, texture_get_texid, RID) BIND1RC(uint32_t, texture_get_width, RID) BIND1RC(uint32_t, texture_get_height, RID) - BIND3(texture_set_size_override, RID, int, int) + BIND1RC(uint32_t, texture_get_depth, RID) + BIND4(texture_set_size_override, RID, int, int, int) BIND3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *) BIND3(texture_set_detect_srgb_callback, RID, TextureDetectCallback, void *) @@ -660,7 +662,7 @@ public: virtual void request_frame_drawn_callback(Object *p_where, const StringName &p_method, const Variant &p_userdata); - virtual void draw(bool p_swap_buffers); + virtual void draw(bool p_swap_buffers, double frame_step); virtual void sync(); virtual bool has_changed() const; virtual void init(); diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 34a1f1458a..213b3ad8f6 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -589,7 +589,7 @@ void VisualServerScene::instance_set_blend_shape_weight(RID p_instance, int p_sh } ERR_FAIL_INDEX(p_shape, instance->blend_values.size()); - instance->blend_values[p_shape] = p_weight; + instance->blend_values.write[p_shape] = p_weight; } void VisualServerScene::instance_set_surface_material(RID p_instance, int p_surface, RID p_material) { @@ -606,7 +606,7 @@ void VisualServerScene::instance_set_surface_material(RID p_instance, int p_surf if (instance->materials[p_surface].is_valid()) { VSG::storage->material_remove_instance_owner(instance->materials[p_surface], instance); } - instance->materials[p_surface] = p_material; + instance->materials.write[p_surface] = p_material; instance->base_material_changed(); if (instance->materials[p_surface].is_valid()) { @@ -820,6 +820,11 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF instance->baked_light = p_enabled; } break; + case VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE: { + + instance->redraw_if_visible = p_enabled; + + } break; } } void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) { @@ -1248,7 +1253,7 @@ void VisualServerScene::_update_instance_lightmap_captures(Instance *p_instance) Vector3 dir = to_cell_xform.basis.xform(cone_traces[i]).normalized(); Color capture = _light_capture_voxel_cone_trace(octree_r.ptr(), pos, dir, cone_aperture, cell_subdiv); - p_instance->lightmap_capture_data[i] += capture; + p_instance->lightmap_capture_data.write[i] += capture; } } } @@ -1459,14 +1464,14 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons light_frustum_planes.resize(6); //right/left - light_frustum_planes[0] = Plane(x_vec, x_max); - light_frustum_planes[1] = Plane(-x_vec, -x_min); + light_frustum_planes.write[0] = Plane(x_vec, x_max); + light_frustum_planes.write[1] = Plane(-x_vec, -x_min); //top/bottom - light_frustum_planes[2] = Plane(y_vec, y_max); - light_frustum_planes[3] = Plane(-y_vec, -y_min); + light_frustum_planes.write[2] = Plane(y_vec, y_max); + light_frustum_planes.write[3] = Plane(-y_vec, -y_min); //near/far - light_frustum_planes[4] = Plane(z_vec, z_max + 1e6); - light_frustum_planes[5] = Plane(-z_vec, -z_min); // z_min is ok, since casters further than far-light plane are not needed + light_frustum_planes.write[4] = Plane(z_vec, z_max + 1e6); + light_frustum_planes.write[5] = Plane(-z_vec, -z_min); // z_min is ok, since casters further than far-light plane are not needed int cull_count = p_scenario->octree.cull_convex(light_frustum_planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK); @@ -1527,11 +1532,11 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons float z = i == 0 ? -1 : 1; Vector<Plane> planes; planes.resize(5); - planes[0] = light_transform.xform(Plane(Vector3(0, 0, z), radius)); - planes[1] = light_transform.xform(Plane(Vector3(1, 0, z).normalized(), radius)); - planes[2] = light_transform.xform(Plane(Vector3(-1, 0, z).normalized(), radius)); - planes[3] = light_transform.xform(Plane(Vector3(0, 1, z).normalized(), radius)); - planes[4] = light_transform.xform(Plane(Vector3(0, -1, z).normalized(), radius)); + planes.write[0] = light_transform.xform(Plane(Vector3(0, 0, z), radius)); + planes.write[1] = light_transform.xform(Plane(Vector3(1, 0, z).normalized(), radius)); + planes.write[2] = light_transform.xform(Plane(Vector3(-1, 0, z).normalized(), radius)); + planes.write[3] = light_transform.xform(Plane(Vector3(0, 1, z).normalized(), radius)); + planes.write[4] = light_transform.xform(Plane(Vector3(0, -1, z).normalized(), radius)); int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK); Plane near_plane(light_transform.origin, light_transform.basis.get_axis(2) * z); @@ -1873,6 +1878,10 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(ins->base_data); + if (ins->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } + if (ins->base_type == VS::INSTANCE_PARTICLES) { //particles visible? process them VSG::storage->particles_request_process(ins->base); @@ -1889,7 +1898,7 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca InstanceLightData *light = static_cast<InstanceLightData *>(E->get()->base_data); - ins->light_instances[l++] = light->instance; + ins->light_instances.write[l++] = light->instance; } geom->lighting_dirty = false; @@ -1904,7 +1913,7 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(E->get()->base_data); - ins->reflection_probe_instances[l++] = reflection_probe->instance; + ins->reflection_probe_instances.write[l++] = reflection_probe->instance; } geom->reflection_dirty = false; @@ -1919,7 +1928,7 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(E->get()->base_data); - ins->gi_probe_instances[l++] = gi_probe->probe_instance; + ins->gi_probe_instances.write[l++] = gi_probe->probe_instance; } geom->gi_probes_dirty = false; @@ -2363,7 +2372,7 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { uint32_t key = blockz * blockw * blockh + blocky * blockw + blockx; - Map<uint32_t, InstanceGIProbeData::CompBlockS3TC> &cmap = comp_blocks[mipmap]; + Map<uint32_t, InstanceGIProbeData::CompBlockS3TC> &cmap = comp_blocks.write[mipmap]; if (!cmap.has(key)) { @@ -2383,8 +2392,8 @@ void VisualServerScene::_setup_gi_probe(Instance *p_instance) { for (int i = 0; i < mipmap_count; i++) { print_line("S3TC level: " + itos(i) + " blocks: " + itos(comp_blocks[i].size())); - probe->dynamic.mipmaps_s3tc[i].resize(comp_blocks[i].size()); - PoolVector<InstanceGIProbeData::CompBlockS3TC>::Write w = probe->dynamic.mipmaps_s3tc[i].write(); + probe->dynamic.mipmaps_s3tc.write[i].resize(comp_blocks[i].size()); + PoolVector<InstanceGIProbeData::CompBlockS3TC>::Write w = probe->dynamic.mipmaps_s3tc.write[i].write(); int block_idx = 0; for (Map<uint32_t, InstanceGIProbeData::CompBlockS3TC>::Element *E = comp_blocks[i].front(); E; E = E->next()) { @@ -2852,7 +2861,7 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { int level_cell_count = probe_data->dynamic.level_cell_lists[i].size(); const uint32_t *level_cells = probe_data->dynamic.level_cell_lists[i].ptr(); - PoolVector<uint8_t>::Write lw = probe_data->dynamic.mipmaps_3d[stage].write(); + PoolVector<uint8_t>::Write lw = probe_data->dynamic.mipmaps_3d.write[stage].write(); uint8_t *mipmapw = lw.ptr(); uint32_t sizes[3] = { header->width >> stage, header->height >> stage, header->depth >> stage }; @@ -2881,7 +2890,7 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { for (int mmi = 0; mmi < mipmap_count; mmi++) { - PoolVector<uint8_t>::Write mmw = probe_data->dynamic.mipmaps_3d[mmi].write(); + PoolVector<uint8_t>::Write mmw = probe_data->dynamic.mipmaps_3d.write[mmi].write(); int block_count = probe_data->dynamic.mipmaps_s3tc[mmi].size(); PoolVector<InstanceGIProbeData::CompBlockS3TC>::Read mmr = probe_data->dynamic.mipmaps_s3tc[mmi].read(); @@ -3214,7 +3223,7 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) { if (new_blend_shape_count != p_instance->blend_values.size()) { p_instance->blend_values.resize(new_blend_shape_count); for (int i = 0; i < new_blend_shape_count; i++) { - p_instance->blend_values[i] = 0; + p_instance->blend_values.write[i] = 0; } } } diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index dd6bc3cf26..a700fcf11b 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -252,7 +252,9 @@ void VisualServerViewport::draw_viewports() { // process all our active interfaces ARVRServer::get_singleton()->_process(); - clear_color = GLOBAL_GET("rendering/environment/default_clear_color"); + if (Engine::get_singleton()->is_editor_hint()) { + clear_color = GLOBAL_GET("rendering/environment/default_clear_color"); + } //sort viewports active_viewports.sort_custom<ViewportSort>(); @@ -660,5 +662,9 @@ bool VisualServerViewport::free(RID p_rid) { return false; } +void VisualServerViewport::set_default_clear_color(const Color &p_color) { + clear_color = p_color; +} + VisualServerViewport::VisualServerViewport() { } diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h index c0c83c0450..f915e26b81 100644 --- a/servers/visual/visual_server_viewport.h +++ b/servers/visual/visual_server_viewport.h @@ -188,6 +188,7 @@ public: virtual int viewport_get_render_info(RID p_viewport, VS::ViewportRenderInfo p_info); virtual void viewport_set_debug_draw(RID p_viewport, VS::ViewportDebugDraw p_draw); + void set_default_clear_color(const Color &p_color); void draw_viewports(); bool free(RID p_rid); diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp index 094e2794ed..93f3792bdc 100644 --- a/servers/visual/visual_server_wrap_mt.cpp +++ b/servers/visual/visual_server_wrap_mt.cpp @@ -37,11 +37,11 @@ void VisualServerWrapMT::thread_exit() { exit = true; } -void VisualServerWrapMT::thread_draw() { +void VisualServerWrapMT::thread_draw(bool p_swap_buffers, double frame_step) { if (!atomic_decrement(&draw_pending)) { - visual_server->draw(); + visual_server->draw(p_swap_buffers, frame_step); } } @@ -91,15 +91,15 @@ void VisualServerWrapMT::sync() { } } -void VisualServerWrapMT::draw(bool p_swap_buffers) { +void VisualServerWrapMT::draw(bool p_swap_buffers, double frame_step) { if (create_thread) { atomic_increment(&draw_pending); - command_queue.push(this, &VisualServerWrapMT::thread_draw); + command_queue.push(this, &VisualServerWrapMT::thread_draw, p_swap_buffers, frame_step); } else { - visual_server->draw(p_swap_buffers); + visual_server->draw(p_swap_buffers, frame_step); } } diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 48f0ec46f3..3a4d72c793 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -55,7 +55,7 @@ class VisualServerWrapMT : public VisualServer { bool create_thread; uint64_t draw_pending; - void thread_draw(); + void thread_draw(bool p_swap_buffers, double frame_step); void thread_flush(); void thread_exit(); @@ -82,17 +82,19 @@ public: /* EVENT QUEUING */ FUNCRID(texture) - FUNC5(texture_allocate, RID, int, int, Image::Format, uint32_t) - FUNC3(texture_set_data, RID, const Ref<Image> &, CubeMapSide) - FUNC10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, CubeMapSide) - FUNC2RC(Ref<Image>, texture_get_data, RID, CubeMapSide) + FUNC7(texture_allocate, RID, int, int, int, Image::Format, TextureType, uint32_t) + FUNC3(texture_set_data, RID, const Ref<Image> &, int) + FUNC10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, int) + FUNC2RC(Ref<Image>, texture_get_data, RID, int) FUNC2(texture_set_flags, RID, uint32_t) FUNC1RC(uint32_t, texture_get_flags, RID) FUNC1RC(Image::Format, texture_get_format, RID) + FUNC1RC(TextureType, texture_get_type, RID) FUNC1RC(uint32_t, texture_get_texid, RID) FUNC1RC(uint32_t, texture_get_width, RID) FUNC1RC(uint32_t, texture_get_height, RID) - FUNC3(texture_set_size_override, RID, int, int) + FUNC1RC(uint32_t, texture_get_depth, RID) + FUNC4(texture_set_size_override, RID, int, int, int) FUNC3(texture_set_detect_3d_callback, RID, TextureDetectCallback, void *) FUNC3(texture_set_detect_srgb_callback, RID, TextureDetectCallback, void *) @@ -578,7 +580,7 @@ public: virtual void init(); virtual void finish(); - virtual void draw(bool p_swap_buffers); + virtual void draw(bool p_swap_buffers, double frame_step); virtual void sync(); FUNC0RC(bool, has_changed) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index a48f1cccae..bc9e9042ec 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -55,7 +55,7 @@ RID VisualServer::texture_create_from_image(const Ref<Image> &p_image, uint32_t ERR_FAIL_COND_V(!p_image.is_valid(), RID()); RID texture = texture_create(); - texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), p_flags); //if it has mipmaps, use, else generate + texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_flags); //if it has mipmaps, use, else generate ERR_FAIL_COND_V(!texture.is_valid(), texture); texture_set_data(texture, p_image); @@ -72,7 +72,9 @@ Array VisualServer::_texture_debug_usage_bind() { Dictionary dict; dict["texture"] = E->get().texture; - dict["size"] = E->get().size; + dict["width"] = E->get().width; + dict["height"] = E->get().height; + dict["depth"] = E->get().depth; dict["format"] = E->get().format; dict["bytes"] = E->get().bytes; dict["path"] = E->get().path; @@ -187,16 +189,14 @@ RID VisualServer::_make_test_cube() { PoolVector<float> tangents; PoolVector<Vector3> uvs; - int vtx_idx = 0; -#define ADD_VTX(m_idx) \ - vertices.push_back(face_points[m_idx]); \ - normals.push_back(normal_points[m_idx]); \ - tangents.push_back(normal_points[m_idx][1]); \ - tangents.push_back(normal_points[m_idx][2]); \ - tangents.push_back(normal_points[m_idx][0]); \ - tangents.push_back(1.0); \ - uvs.push_back(Vector3(uv_points[m_idx * 2 + 0], uv_points[m_idx * 2 + 1], 0)); \ - vtx_idx++; +#define ADD_VTX(m_idx) \ + vertices.push_back(face_points[m_idx]); \ + normals.push_back(normal_points[m_idx]); \ + tangents.push_back(normal_points[m_idx][1]); \ + tangents.push_back(normal_points[m_idx][2]); \ + tangents.push_back(normal_points[m_idx][0]); \ + tangents.push_back(1.0); \ + uvs.push_back(Vector3(uv_points[m_idx * 2 + 0], uv_points[m_idx * 2 + 1], 0)); for (int i = 0; i < 6; i++) { @@ -335,7 +335,7 @@ RID VisualServer::get_white_texture() { } Ref<Image> white = memnew(Image(4, 4, 0, Image::FORMAT_RGB8, wt)); white_texture = texture_create(); - texture_allocate(white_texture, 4, 4, Image::FORMAT_RGB8); + texture_allocate(white_texture, 4, 4, 0, Image::FORMAT_RGB8, TEXTURE_TYPE_2D); texture_set_data(white_texture, white); return white_texture; } @@ -746,7 +746,7 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_ if (first) { for (int i = 0; i < total_bones; i++) { - r_bone_aabb[i].size = Vector3(-1, -1, -1); //negative means unused + r_bone_aabb.write[i].size = Vector3(-1, -1, -1); //negative means unused } } @@ -795,6 +795,140 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_ return OK; } +uint32_t VisualServer::mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const { + uint32_t offsets[ARRAY_MAX]; + mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets); + return offsets[p_array_index]; +} + +uint32_t VisualServer::mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const { + uint32_t offsets[ARRAY_MAX]; + return mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets); +} + +uint32_t VisualServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const { + + int total_elem_size = 0; + + for (int i = 0; i < VS::ARRAY_MAX; i++) { + + r_offsets[i] = 0; //reset + + if (!(p_format & (1 << i))) // no array + continue; + + int elem_size = 0; + + switch (i) { + + case VS::ARRAY_VERTEX: { + + if (p_format & ARRAY_FLAG_USE_2D_VERTICES) { + elem_size = 2; + } else { + elem_size = 3; + } + + if (p_format & ARRAY_COMPRESS_VERTEX) { + elem_size *= sizeof(int16_t); + } else { + elem_size *= sizeof(float); + } + + if (elem_size == 6) { + elem_size = 8; + } + + } break; + case VS::ARRAY_NORMAL: { + + if (p_format & ARRAY_COMPRESS_NORMAL) { + elem_size = sizeof(uint32_t); + } else { + elem_size = sizeof(float) * 3; + } + + } break; + + case VS::ARRAY_TANGENT: { + if (p_format & ARRAY_COMPRESS_TANGENT) { + elem_size = sizeof(uint32_t); + } else { + elem_size = sizeof(float) * 4; + } + + } break; + case VS::ARRAY_COLOR: { + + if (p_format & ARRAY_COMPRESS_COLOR) { + elem_size = sizeof(uint32_t); + } else { + elem_size = sizeof(float) * 4; + } + } break; + case VS::ARRAY_TEX_UV: { + if (p_format & ARRAY_COMPRESS_TEX_UV) { + elem_size = sizeof(uint32_t); + } else { + elem_size = sizeof(float) * 2; + } + + } break; + + case VS::ARRAY_TEX_UV2: { + if (p_format & ARRAY_COMPRESS_TEX_UV2) { + elem_size = sizeof(uint32_t); + } else { + elem_size = sizeof(float) * 2; + } + + } break; + case VS::ARRAY_WEIGHTS: { + + if (p_format & ARRAY_COMPRESS_WEIGHTS) { + elem_size = sizeof(uint16_t) * 4; + } else { + elem_size = sizeof(float) * 4; + } + + } break; + case VS::ARRAY_BONES: { + + if (p_format & ARRAY_FLAG_USE_16_BIT_BONES) { + elem_size = sizeof(uint16_t) * 4; + } else { + elem_size = sizeof(uint32_t); + } + + } break; + case VS::ARRAY_INDEX: { + + if (p_index_len <= 0) { + ERR_PRINT("index_array_len==NO_INDEX_ARRAY"); + break; + } + /* determine whether using 16 or 32 bits indices */ + if (p_vertex_len >= (1 << 16)) { + + elem_size = 4; + + } else { + elem_size = 2; + } + r_offsets[i] = elem_size; + continue; + } break; + default: { + ERR_FAIL_V(0); + } + } + + r_offsets[i] = total_elem_size; + total_elem_size += elem_size; + } + return total_elem_size; +} + void VisualServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, uint32_t p_compress_format) { ERR_FAIL_INDEX(p_primitive, VS::PRIMITIVE_MAX); @@ -1516,27 +1650,29 @@ Array VisualServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surfa void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("force_sync"), &VisualServer::sync); - ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers"), &VisualServer::draw, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("force_draw", "swap_buffers", "frame_step"), &VisualServer::draw, DEFVAL(true), DEFVAL(0.0)); // "draw" and "sync" are deprecated duplicates of "force_draw" and "force_sync" // FIXME: Add deprecation messages using GH-4397 once available, and retire // once the warnings have been enabled for a full release cycle ClassDB::bind_method(D_METHOD("sync"), &VisualServer::sync); - ClassDB::bind_method(D_METHOD("draw", "swap_buffers"), &VisualServer::draw, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("draw", "swap_buffers", "frame_step"), &VisualServer::draw, DEFVAL(true), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("texture_create"), &VisualServer::texture_create); ClassDB::bind_method(D_METHOD("texture_create_from_image", "image", "flags"), &VisualServer::texture_create_from_image, DEFVAL(TEXTURE_FLAGS_DEFAULT)); - ClassDB::bind_method(D_METHOD("texture_allocate", "texture", "width", "height", "format", "flags"), &VisualServer::texture_allocate, DEFVAL(TEXTURE_FLAGS_DEFAULT)); - ClassDB::bind_method(D_METHOD("texture_set_data", "texture", "image", "cube_side"), &VisualServer::texture_set_data, DEFVAL(CUBEMAP_LEFT)); - ClassDB::bind_method(D_METHOD("texture_set_data_partial", "texture", "image", "src_x", "src_y", "src_w", "src_h", "dst_x", "dst_y", "dst_mip", "cube_side"), &VisualServer::texture_set_data_partial, DEFVAL(CUBEMAP_LEFT)); + ClassDB::bind_method(D_METHOD("texture_allocate", "texture", "width", "height", "depth_3d", "format", "type", "flags"), &VisualServer::texture_allocate, DEFVAL(TEXTURE_FLAGS_DEFAULT)); + ClassDB::bind_method(D_METHOD("texture_set_data", "texture", "image", "layer"), &VisualServer::texture_set_data, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("texture_set_data_partial", "texture", "image", "src_x", "src_y", "src_w", "src_h", "dst_x", "dst_y", "dst_mip", "layer"), &VisualServer::texture_set_data_partial, DEFVAL(0)); ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "cube_side"), &VisualServer::texture_get_data, DEFVAL(CUBEMAP_LEFT)); ClassDB::bind_method(D_METHOD("texture_set_flags", "texture", "flags"), &VisualServer::texture_set_flags); ClassDB::bind_method(D_METHOD("texture_get_flags", "texture"), &VisualServer::texture_get_flags); ClassDB::bind_method(D_METHOD("texture_get_format", "texture"), &VisualServer::texture_get_format); + ClassDB::bind_method(D_METHOD("texture_get_type", "texture"), &VisualServer::texture_get_type); ClassDB::bind_method(D_METHOD("texture_get_texid", "texture"), &VisualServer::texture_get_texid); ClassDB::bind_method(D_METHOD("texture_get_width", "texture"), &VisualServer::texture_get_width); ClassDB::bind_method(D_METHOD("texture_get_height", "texture"), &VisualServer::texture_get_height); - ClassDB::bind_method(D_METHOD("texture_set_size_override", "texture", "width", "height"), &VisualServer::texture_set_size_override); + ClassDB::bind_method(D_METHOD("texture_get_depth", "texture"), &VisualServer::texture_get_depth); + ClassDB::bind_method(D_METHOD("texture_set_size_override", "texture", "width", "height", "depth"), &VisualServer::texture_set_size_override); ClassDB::bind_method(D_METHOD("texture_set_path", "texture", "path"), &VisualServer::texture_set_path); ClassDB::bind_method(D_METHOD("texture_get_path", "texture"), &VisualServer::texture_get_path); ClassDB::bind_method(D_METHOD("texture_set_shrink_all_x2_on_set_data", "shrink"), &VisualServer::texture_set_shrink_all_x2_on_set_data); @@ -1564,11 +1700,14 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("material_set_next_pass", "material", "next_material"), &VisualServer::material_set_next_pass); ClassDB::bind_method(D_METHOD("mesh_create"), &VisualServer::mesh_create); + ClassDB::bind_method(D_METHOD("mesh_surface_get_format_offset", "format", "vertex_len", "index_len", "array_index"), &VisualServer::mesh_surface_get_format_offset); + ClassDB::bind_method(D_METHOD("mesh_surface_get_format_stride", "format", "vertex_len", "index_len"), &VisualServer::mesh_surface_get_format_stride); ClassDB::bind_method(D_METHOD("mesh_add_surface_from_arrays", "mesh", "primtive", "arrays", "blend_shapes", "compress_format"), &VisualServer::mesh_add_surface_from_arrays, DEFVAL(Array()), DEFVAL(ARRAY_COMPRESS_DEFAULT)); ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_count", "mesh", "amount"), &VisualServer::mesh_set_blend_shape_count); ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_count", "mesh"), &VisualServer::mesh_get_blend_shape_count); ClassDB::bind_method(D_METHOD("mesh_set_blend_shape_mode", "mesh", "mode"), &VisualServer::mesh_set_blend_shape_mode); ClassDB::bind_method(D_METHOD("mesh_get_blend_shape_mode", "mesh"), &VisualServer::mesh_get_blend_shape_mode); + ClassDB::bind_method(D_METHOD("mesh_surface_update_region", "mesh", "surface", "offset", "data"), &VisualServer::mesh_surface_update_region); ClassDB::bind_method(D_METHOD("mesh_surface_set_material", "mesh", "surface", "material"), &VisualServer::mesh_surface_set_material); ClassDB::bind_method(D_METHOD("mesh_surface_get_material", "mesh", "surface"), &VisualServer::mesh_surface_get_material); ClassDB::bind_method(D_METHOD("mesh_surface_get_array_len", "mesh", "surface"), &VisualServer::mesh_surface_get_array_len); @@ -1773,7 +1912,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &VisualServer::environment_set_tonemap); ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "ramp"), &VisualServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance", "roughness"), &VisualServer::environment_set_ssr); - ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "radius2", "intensity2", "bias", "light_affect", "color", "quality", "blur", "bilateral_sharpness"), &VisualServer::environment_set_ssao); + ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "radius2", "intensity2", "bias", "light_affect", "ao_channel_affect", "color", "quality", "blur", "bilateral_sharpness"), &VisualServer::environment_set_ssao); ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "color", "sun_color", "sun_amount"), &VisualServer::environment_set_fog); ClassDB::bind_method(D_METHOD("environment_set_fog_depth", "env", "enable", "depth_begin", "depth_curve", "transmit", "transmit_curve"), &VisualServer::environment_set_fog_depth); ClassDB::bind_method(D_METHOD("environment_set_fog_height", "env", "enable", "min_height", "max_height", "height_curve"), &VisualServer::environment_set_fog_height); @@ -1924,13 +2063,17 @@ void VisualServer::_bind_methods() { BIND_ENUM_CONSTANT(CUBEMAP_FRONT); BIND_ENUM_CONSTANT(CUBEMAP_BACK); + BIND_ENUM_CONSTANT(TEXTURE_TYPE_2D); + BIND_ENUM_CONSTANT(TEXTURE_TYPE_CUBEMAP); + BIND_ENUM_CONSTANT(TEXTURE_TYPE_2D_ARRAY); + BIND_ENUM_CONSTANT(TEXTURE_TYPE_3D); + BIND_ENUM_CONSTANT(TEXTURE_FLAG_MIPMAPS); BIND_ENUM_CONSTANT(TEXTURE_FLAG_REPEAT); BIND_ENUM_CONSTANT(TEXTURE_FLAG_FILTER); BIND_ENUM_CONSTANT(TEXTURE_FLAG_ANISOTROPIC_FILTER); BIND_ENUM_CONSTANT(TEXTURE_FLAG_CONVERT_TO_LINEAR); BIND_ENUM_CONSTANT(TEXTURE_FLAG_MIRRORED_REPEAT); - BIND_ENUM_CONSTANT(TEXTURE_FLAG_CUBEMAP); BIND_ENUM_CONSTANT(TEXTURE_FLAG_USED_FOR_STREAMING); BIND_ENUM_CONSTANT(TEXTURE_FLAGS_DEFAULT); @@ -2066,6 +2209,7 @@ void VisualServer::_bind_methods() { BIND_ENUM_CONSTANT(INSTANCE_GEOMETRY_MASK); BIND_ENUM_CONSTANT(INSTANCE_FLAG_USE_BAKED_LIGHT); + BIND_ENUM_CONSTANT(INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE); BIND_ENUM_CONSTANT(INSTANCE_FLAG_MAX); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); diff --git a/servers/visual_server.h b/servers/visual_server.h index 968cb852ed..0ec902c18c 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -90,11 +90,17 @@ public: TEXTURE_FLAG_ANISOTROPIC_FILTER = 8, TEXTURE_FLAG_CONVERT_TO_LINEAR = 16, TEXTURE_FLAG_MIRRORED_REPEAT = 32, /// Repeat texture, with alternate sections mirrored - TEXTURE_FLAG_CUBEMAP = 2048, - TEXTURE_FLAG_USED_FOR_STREAMING = 4096, + TEXTURE_FLAG_USED_FOR_STREAMING = 2048, TEXTURE_FLAGS_DEFAULT = TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS | TEXTURE_FLAG_FILTER }; + enum TextureType { + TEXTURE_TYPE_2D, + TEXTURE_TYPE_CUBEMAP, + TEXTURE_TYPE_2D_ARRAY, + TEXTURE_TYPE_3D, + }; + enum CubeMapSide { CUBEMAP_LEFT, @@ -107,17 +113,33 @@ public: virtual RID texture_create() = 0; RID texture_create_from_image(const Ref<Image> &p_image, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT); // helper - virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT) = 0; - virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, CubeMapSide p_cube_side = CUBEMAP_LEFT) = 0; - virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, CubeMapSide p_cube_side = CUBEMAP_LEFT) = 0; - virtual Ref<Image> texture_get_data(RID p_texture, CubeMapSide p_cube_side = CUBEMAP_LEFT) const = 0; + virtual void texture_allocate(RID p_texture, + int p_width, + int p_height, + int p_depth_3d, + Image::Format p_format, + TextureType p_type, + uint32_t p_flags = TEXTURE_FLAGS_DEFAULT) = 0; + + virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; + virtual void texture_set_data_partial(RID p_texture, + const Ref<Image> &p_image, + int src_x, int src_y, + int src_w, int src_h, + int dst_x, int dst_y, + int p_dst_mip, + int p_layer = 0) = 0; + + virtual Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const = 0; virtual void texture_set_flags(RID p_texture, uint32_t p_flags) = 0; virtual uint32_t texture_get_flags(RID p_texture) const = 0; virtual Image::Format texture_get_format(RID p_texture) const = 0; + virtual TextureType texture_get_type(RID p_texture) const = 0; virtual uint32_t texture_get_texid(RID p_texture) const = 0; virtual uint32_t texture_get_width(RID p_texture) const = 0; virtual uint32_t texture_get_height(RID p_texture) const = 0; - virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) = 0; + virtual uint32_t texture_get_depth(RID p_texture) const = 0; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height, int p_depth_3d) = 0; virtual void texture_set_path(RID p_texture, const String &p_path) = 0; virtual String texture_get_path(RID p_texture) const = 0; @@ -132,7 +154,9 @@ public: struct TextureInfo { RID texture; - Size2 size; + uint32_t width; + uint32_t height; + uint32_t depth; Image::Format format; int bytes; String path; @@ -251,6 +275,10 @@ public: virtual RID mesh_create() = 0; + virtual uint32_t mesh_surface_get_format_offset(uint32_t p_format, int p_vertex_len, int p_index_len, int p_array_index) const; + virtual uint32_t mesh_surface_get_format_stride(uint32_t p_format, int p_vertex_len, int p_index_len) const; + /// Returns stride + virtual uint32_t mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets) const; virtual void mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), uint32_t p_compress_format = ARRAY_COMPRESS_DEFAULT); virtual void mesh_add_surface(RID p_mesh, uint32_t p_format, PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>()) = 0; @@ -805,6 +833,7 @@ public: enum InstanceFlags { INSTANCE_FLAG_USE_BAKED_LIGHT, + INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, INSTANCE_FLAG_MAX }; @@ -950,7 +979,7 @@ public: /* EVENT QUEUING */ - virtual void draw(bool p_swap_buffers = true) = 0; + virtual void draw(bool p_swap_buffers = true, double frame_step = 0.0) = 0; virtual void sync() = 0; virtual bool has_changed() const = 0; virtual void init() = 0; @@ -1049,6 +1078,7 @@ VARIANT_ENUM_CAST(VisualServer::EnvironmentSSAOQuality); VARIANT_ENUM_CAST(VisualServer::EnvironmentSSAOBlur); VARIANT_ENUM_CAST(VisualServer::InstanceFlags); VARIANT_ENUM_CAST(VisualServer::ShadowCastingSetting); +VARIANT_ENUM_CAST(VisualServer::TextureType); //typedef VisualServer VS; // makes it easier to use #define VS VisualServer diff --git a/thirdparty/README.md b/thirdparty/README.md index ea2a996812..11745bc532 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -117,7 +117,7 @@ Files extracted from upstream source: ## glad - Upstream: https://github.com/Dav1dde/glad -- Version: 0.1.20a0 +- Version: 0.1.25 - License: MIT The files we package are automatically generated. @@ -152,7 +152,7 @@ Files extracted from upstream source: ## libpng - Upstream: http://libpng.org/pub/png/libpng.html -- Version: 1.6.34 +- Version: 1.6.35 - License: libpng/zlib Files extracted from upstream source: @@ -161,6 +161,7 @@ Files extracted from upstream source: `example.c` and `pngtest.c` - the arm/ folder - `scripts/pnglibconf.h.prebuilt` as `pnglibconf.h` +- `LICENSE` ## libsimplewebm @@ -262,14 +263,12 @@ Godot build configurations, check them out when updating. ## mbedtls - Upstream: https://tls.mbed.org/ -- Version: 2.8.0 +- Version: 2.12.0 - License: Apache 2.0 -File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`: +File extracted from upstream release tarball `mbedtls-2.12.0-apache.tgz`: - All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/` - All `*.c` from `library/` to `thirdparty/mbedtls/library/` -- In file `thirdparty/mbedtls/library/net_sockets.c` mbedTLS overrides the `_WIN32_WINNT` define. - Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream). - Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update. diff --git a/thirdparty/glad/glad.c b/thirdparty/glad/glad.c index 8f8b2189ec..dd6fe8b8f3 100644 --- a/thirdparty/glad/glad.c +++ b/thirdparty/glad/glad.c @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.20a0 on Fri May 4 21:44:11 2018. + OpenGL loader generated by glad 0.1.25 on Sat Jul 28 10:59:43 2018. Language/Generator: C/C++ Specification: gl @@ -56,8 +56,9 @@ int open_gl(void) { #ifndef IS_UWP libGL = LoadLibraryW(L"opengl32.dll"); if(libGL != NULL) { - gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE)GetProcAddress( - libGL, "wglGetProcAddress"); + void (* tmp)(void); + tmp = (void(*)(void)) GetProcAddress(libGL, "wglGetProcAddress"); + gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp; return gladGetProcAddressPtr != NULL; } #endif @@ -164,7 +165,7 @@ static int max_loaded_minor; static const char *exts = NULL; static int num_exts_i = 0; -static const char **exts_i = NULL; +static char **exts_i = NULL; static int get_exts(void) { #ifdef _GLAD_IS_SOME_NEW_VERSION @@ -178,7 +179,7 @@ static int get_exts(void) { num_exts_i = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); if (num_exts_i > 0) { - exts_i = (const char **)realloc((void *)exts_i, (size_t)num_exts_i * (sizeof *exts_i)); + exts_i = (char **)realloc((void *)exts_i, (size_t)num_exts_i * (sizeof *exts_i)); } if (exts_i == NULL) { @@ -191,11 +192,7 @@ static int get_exts(void) { char *local_str = (char*)malloc((len+1) * sizeof(char)); if(local_str != NULL) { -#if _MSC_VER >= 1400 - strncpy_s(local_str, len+1, gl_str_tmp, len); -#else - strncpy(local_str, gl_str_tmp, len+1); -#endif + memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char)); } exts_i[index] = local_str; } diff --git a/thirdparty/glad/glad/glad.h b/thirdparty/glad/glad/glad.h index 4eebad4f2f..4d92d33b37 100644 --- a/thirdparty/glad/glad/glad.h +++ b/thirdparty/glad/glad/glad.h @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.20a0 on Fri May 4 21:44:11 2018. + OpenGL loader generated by glad 0.1.25 on Sat Jul 28 10:59:43 2018. Language/Generator: C/C++ Specification: gl @@ -156,16 +156,8 @@ typedef unsigned int GLhandleARB; typedef unsigned short GLhalfARB; typedef unsigned short GLhalf; typedef GLint GLfixed; -#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) -typedef long GLintptr; -#else -typedef ptrdiff_t GLintptr; -#endif -#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) -typedef long GLsizeiptr; -#else -typedef ptrdiff_t GLsizeiptr; -#endif +typedef khronos_intptr_t GLintptr; +typedef khronos_ssize_t GLsizeiptr; typedef int64_t GLint64; typedef uint64_t GLuint64; #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) diff --git a/thirdparty/libpng/LICENSE b/thirdparty/libpng/LICENSE index 4cda4fa0ad..6ee9c8f554 100644 --- a/thirdparty/libpng/LICENSE +++ b/thirdparty/libpng/LICENSE @@ -10,8 +10,8 @@ this sentence. This code is released under the libpng license. -libpng versions 1.0.7, July 1, 2000 through 1.6.34, September 29, 2017 are -Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are +libpng versions 1.0.7, July 1, 2000 through 1.6.35, July 15, 2018 are +Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are derived from libpng-1.0.6, and are distributed according to the same disclaimer and license as libpng-1.0.6 with the following individuals added to the list of Contributing Authors: @@ -130,4 +130,4 @@ any encryption software. See the EAR, paragraphs 734.3(b)(3) and Glenn Randers-Pehrson glennrp at users.sourceforge.net -September 29, 2017 +July 15, 2018 diff --git a/thirdparty/libpng/png.c b/thirdparty/libpng/png.c index ff02c56518..a25afebcc8 100644 --- a/thirdparty/libpng/png.c +++ b/thirdparty/libpng/png.c @@ -1,8 +1,8 @@ /* png.c - location for general purpose libpng functions * - * Last changed in libpng 1.6.33 [September 28, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -14,7 +14,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_34 Your_png_h_is_not_version_1_6_34; +typedef png_libpng_version_1_6_35 Your_png_h_is_not_version_1_6_35; #ifdef __GNUC__ /* The version tests may need to be added to, but the problem warning has @@ -71,7 +71,7 @@ png_set_sig_bytes(png_structrp png_ptr, int num_bytes) * PNG signature (this is the same behavior as strcmp, memcmp, etc). */ int PNGAPI -png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) +png_sig_cmp(png_const_bytep sig, size_t start, size_t num_to_check) { png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; @@ -136,7 +136,7 @@ png_reset_crc(png_structrp png_ptr) * trouble of calculating it. */ void /* PRIVATE */ -png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length) +png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, size_t length) { int need_crc = 1; @@ -421,7 +421,7 @@ png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr) * those cases where it does anything other than a memset. */ PNG_FUNCTION(void,PNGAPI -png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), +png_info_init_3,(png_infopp ptr_ptr, size_t png_info_struct_size), PNG_DEPRECATED) { png_inforp info_ptr = *ptr_ptr; @@ -816,15 +816,15 @@ png_get_copyright(png_const_structrp png_ptr) #else # ifdef __STDC__ return PNG_STRING_NEWLINE \ - "libpng version 1.6.34 - September 29, 2017" PNG_STRING_NEWLINE \ - "Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson" \ + "libpng version 1.6.35 - July 15, 2018" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ PNG_STRING_NEWLINE; # else - return "libpng version 1.6.34 - September 29, 2017\ - Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson\ + return "libpng version 1.6.35 - July 15, 2018\ + Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson\ Copyright (c) 1996-1997 Andreas Dilger\ Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; # endif @@ -942,7 +942,7 @@ png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) /* The code is the fifth byte after each four byte string. Historically this * code was always searched from the end of the list, this is no longer - * necessary because the 'set' routine handles duplicate entries correcty. + * necessary because the 'set' routine handles duplicate entries correctly. */ do /* num_chunk_list > 0, so at least one */ { @@ -2067,7 +2067,7 @@ png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, */ /* Data checks (could be skipped). These checks must be independent of the - * version number; however, the version number doesn't accomodate changes in + * version number; however, the version number doesn't accommodate changes in * the header fields (just the known tags and the interpretation of the * data.) */ @@ -2707,7 +2707,7 @@ png_check_IHDR(png_const_structrp png_ptr, #if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) /* ASCII to fp functions */ -/* Check an ASCII formated floating point value, see the more detailed +/* Check an ASCII formatted floating point value, see the more detailed * comments in pngpriv.h */ /* The following is used internally to preserve the sticky flags */ @@ -2715,11 +2715,11 @@ png_check_IHDR(png_const_structrp png_ptr, #define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY)) int /* PRIVATE */ -png_check_fp_number(png_const_charp string, png_size_t size, int *statep, +png_check_fp_number(png_const_charp string, size_t size, int *statep, png_size_tp whereami) { int state = *statep; - png_size_t i = *whereami; + size_t i = *whereami; while (i < size) { @@ -2842,10 +2842,10 @@ PNG_FP_End: /* The same but for a complete string. */ int -png_check_fp_string(png_const_charp string, png_size_t size) +png_check_fp_string(png_const_charp string, size_t size) { int state=0; - png_size_t char_index=0; + size_t char_index=0; if (png_check_fp_number(string, size, &state, &char_index) != 0 && (char_index == size || string[char_index] == 0)) @@ -2906,7 +2906,7 @@ png_pow10(int power) #pragma GCC diagnostic warning "-Wstrict-overflow=2" #endif /* GCC_STRICT_OVERFLOW */ void /* PRIVATE */ -png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, +png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, size_t size, double fp, unsigned int precision) { /* We use standard functions from math.h, but not printf because @@ -3237,7 +3237,7 @@ png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, */ void /* PRIVATE */ png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, - png_size_t size, png_fixed_point fp) + size_t size, png_fixed_point fp) { /* Require space for 10 decimal digits, a decimal point, a minus sign and a * trailing \0, 13 characters: @@ -4344,7 +4344,7 @@ png_set_option(png_structrp png_ptr, int option, int onoff) png_uint_32 setting = (2U + (onoff != 0)) << option; png_uint_32 current = png_ptr->options; - png_ptr->options = (png_uint_32)(((current & ~mask) | setting) & 0xff); + png_ptr->options = (png_uint_32)((current & ~mask) | setting); return (int)(current & mask) >> option; } diff --git a/thirdparty/libpng/png.h b/thirdparty/libpng/png.h index 4c873f5c22..19e464cc17 100644 --- a/thirdparty/libpng/png.h +++ b/thirdparty/libpng/png.h @@ -1,9 +1,9 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.34, September 29, 2017 + * libpng version 1.6.35, July 15, 2018 * - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -12,7 +12,7 @@ * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.6.34, September 29, 2017: + * libpng versions 0.97, January 1998, through 1.6.35, July 15, 2018: * Glenn Randers-Pehrson. * See also "Contributing Authors", below. */ @@ -25,8 +25,8 @@ * * This code is released under the libpng license. * - * libpng versions 1.0.7, July 1, 2000 through 1.6.34, September 29, 2017 are - * Copyright (c) 2000-2002, 2004, 2006-2017 Glenn Randers-Pehrson, are + * libpng versions 1.0.7, July 1, 2000 through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are * derived from libpng-1.0.6, and are distributed according to the same * disclaimer and license as libpng-1.0.6 with the following individuals * added to the list of Contributing Authors: @@ -213,7 +213,7 @@ * ... * 1.5.30 15 10527 15.so.15.30[.0] * ... - * 1.6.34 16 10633 16.so.16.34[.0] + * 1.6.35 16 10635 16.so.16.35[.0] * * Henceforth the source version will match the shared-library major * and minor numbers; the shared-library major version number will be @@ -241,13 +241,13 @@ * Y2K compliance in libpng: * ========================= * - * September 29, 2017 + * July 15, 2018 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. * * This is your unofficial assurance that libpng from version 0.71 and - * upward through 1.6.34 are Y2K compliant. It is my belief that + * upward through 1.6.35 are Y2K compliant. It is my belief that * earlier versions were also Y2K compliant. * * Libpng only has two year fields. One is a 2-byte unsigned integer @@ -309,8 +309,8 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.34" -#define PNG_HEADER_VERSION_STRING " libpng version 1.6.34 - September 29, 2017\n" +#define PNG_LIBPNG_VER_STRING "1.6.35" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.35 - July 15, 2018\n" #define PNG_LIBPNG_VER_SONUM 16 #define PNG_LIBPNG_VER_DLLNUM 16 @@ -318,13 +318,13 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 34 +#define PNG_LIBPNG_VER_RELEASE 35 /* This should match the numeric part of the final component of * PNG_LIBPNG_VER_STRING, omitting any leading zero: */ -#define PNG_LIBPNG_VER_BUILD 0 +#define PNG_LIBPNG_VER_BUILD 02 /* Release Status */ #define PNG_LIBPNG_BUILD_ALPHA 1 @@ -341,7 +341,7 @@ #define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with PNG_LIBPNG_BUILD_PRIVATE */ -#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_BETA /* Careful here. At one time, Guy wanted to use 082, but that would be octal. * We must not include leading zeros. @@ -349,7 +349,7 @@ * version 1.0.0 was mis-numbered 100 instead of 10000). From * version 1.0.1 it's xxyyzz, where x=major, y=minor, z=release */ -#define PNG_LIBPNG_VER 10634 /* 1.6.34 */ +#define PNG_LIBPNG_VER 10635 /* 1.6.35 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -459,7 +459,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_34; +typedef char* png_libpng_version_1_6_35; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * @@ -600,8 +600,8 @@ typedef struct png_text_struct png_charp key; /* keyword, 1-79 character description of "text" */ png_charp text; /* comment, may be an empty string (ie "") or a NULL pointer */ - png_size_t text_length; /* length of the text string */ - png_size_t itxt_length; /* length of the itxt string */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ png_charp lang; /* language code, 0-79 characters or a NULL pointer */ png_charp lang_key; /* keyword translated UTF-8 string, 0 or more @@ -654,7 +654,7 @@ typedef struct png_unknown_chunk_t { png_byte name[5]; /* Textual chunk name with '\0' terminator */ png_byte *data; /* Data, should not be modified on read! */ - png_size_t size; + size_t size; /* On write 'location' must be set using the flag values listed below. * Notice that on read it is set by libpng however the values stored have @@ -679,7 +679,7 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; /* Maximum positive integer used in PNG is (2^31)-1 */ #define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) #define PNG_UINT_32_MAX ((png_uint_32)(-1)) -#define PNG_SIZE_MAX ((png_size_t)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) /* These are constants for fixed point values encoded in the * PNG specification manner (x100000) @@ -785,7 +785,7 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; typedef struct png_row_info_struct { png_uint_32 width; /* width of row */ - png_size_t rowbytes; /* number of bytes in row */ + size_t rowbytes; /* number of bytes in row */ png_byte color_type; /* color type of row */ png_byte bit_depth; /* bit depth of row */ png_byte channels; /* number of channels (1, 2, 3, or 4) */ @@ -804,7 +804,7 @@ typedef png_row_info * * png_row_infopp; * expected to return the read data in the buffer. */ typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); -typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, png_size_t)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, int)); @@ -941,8 +941,8 @@ PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); * signature, and non-zero otherwise. Having num_to_check == 0 or * start > 7 will always fail (ie return non-zero). */ -PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, png_size_t start, - png_size_t num_to_check)); +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); /* Simple signature checking function. This is the same as calling * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). @@ -961,11 +961,11 @@ PNG_EXPORTA(5, png_structp, png_create_write_struct, png_error_ptr warn_fn), PNG_ALLOCATED); -PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size, +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, (png_const_structrp png_ptr)); PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, - png_size_t size)); + size_t size)); /* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp * match up. @@ -1018,7 +1018,7 @@ PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); /* Write a PNG chunk - size, type, (optional) data, CRC. */ PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep - chunk_name, png_const_bytep data, png_size_t length)); + chunk_name, png_const_bytep data, size_t length)); /* Write the start of a PNG chunk - length and chunk name. */ PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, @@ -1026,7 +1026,7 @@ PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, /* Write the data of a PNG chunk started with png_write_chunk_start(). */ PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, - png_const_bytep data, png_size_t length)); + png_const_bytep data, size_t length)); /* Finish a chunk started with png_write_chunk_start() (includes CRC). */ PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); @@ -1040,7 +1040,7 @@ PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), * the API will be removed in the future. */ PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, - png_size_t png_info_struct_size), PNG_DEPRECATED); + size_t png_info_struct_size), PNG_DEPRECATED); /* Writes all the PNG information before the image. */ PNG_EXPORT(20, void, png_write_info_before_PLTE, @@ -1137,7 +1137,7 @@ PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, * corresponding composited pixel, and the color channels are unassociated * (not premultiplied). The gamma encoded color channels must be scaled * according to the contribution and to do this it is necessary to undo - * the encoding, scale the color values, perform the composition and reencode + * the encoding, scale the color values, perform the composition and re-encode * the values. This is the 'PNG' mode. * * The alternative is to 'associate' the alpha with the color information by @@ -1193,7 +1193,7 @@ PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, * * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); * In this case the output is assumed to be something like an sRGB conformant - * display preceeded by a power-law lookup table of power 1.45. This is how + * display preceded by a power-law lookup table of power 1.45. This is how * early Mac systems behaved. * * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); @@ -1240,7 +1240,7 @@ PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, * * When the default gamma of PNG files doesn't match the output gamma. * If you have PNG files with no gamma information png_set_alpha_mode allows - * you to provide a default gamma, but it also sets the ouput gamma to the + * you to provide a default gamma, but it also sets the output gamma to the * matching value. If you know your PNG files have a gamma that doesn't * match the output you can take advantage of the fact that * png_set_alpha_mode always sets the output gamma but only sets the PNG @@ -1691,7 +1691,7 @@ PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); * chunk will cause an error at this point unless it is to be saved. * positive: The chunk was handled, libpng will ignore/discard it. * - * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about * how this behavior will change in libpng 1.7 */ PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, @@ -1716,7 +1716,7 @@ PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, /* Function to be called when data becomes available */ PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, - png_inforp info_ptr, png_bytep buffer, png_size_t buffer_size)); + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); /* A function which may be called *only* within png_process_data to stop the * processing of any more data. The function returns the number of bytes @@ -1725,7 +1725,7 @@ PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, * 'save' is set to true the routine will first save all the pending data and * will always return 0. */ -PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save)); +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); /* A function which may be called *only* outside (after) a call to * png_process_data. It returns the number of bytes of data to skip in the @@ -1870,7 +1870,7 @@ PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag)); /* Returns number of bytes needed to hold a transformed row. */ -PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr, +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, png_const_inforp info_ptr)); #ifdef PNG_INFO_IMAGE_SUPPORTED @@ -2239,7 +2239,7 @@ PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks * it simply resets the behavior to the libpng default. * - * INTERACTION WTIH USER CHUNK CALLBACKS: + * INTERACTION WITH USER CHUNK CALLBACKS: * The per-chunk handling is always used when there is a png_user_chunk_ptr * callback and the callback returns 0; the chunk is then always stored *unless* * it is critical and the per-chunk setting is other than ALWAYS. Notice that @@ -2658,7 +2658,7 @@ PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, * The simplified API hides the details of both libpng and the PNG file format * itself. It allows PNG files to be read into a very limited number of * in-memory bitmap formats or to be written from the same formats. If these - * formats do not accomodate your needs then you can, and should, use the more + * formats do not accommodate your needs then you can, and should, use the more * sophisticated APIs above - these support a wide variety of in-memory formats * and a wide variety of sophisticated transformations to those formats as well * as a wide variety of APIs to manipulate ancillary information. @@ -3020,7 +3020,7 @@ PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, #endif /* STDIO */ PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, - png_const_voidp memory, png_size_t size)); + png_const_voidp memory, size_t size)); /* The PNG header is read from the given memory buffer. */ PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, @@ -3133,7 +3133,7 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, * than or equal to the original value. * * If the function returns false and *memory_bytes was not changed an error - * occured during write. If *memory_bytes was changed, or is not 0 if + * occurred during write. If *memory_bytes was changed, or is not 0 if * 'memory' was NULL, the write would have succeeded but for the memory * buffer being too small. *memory_bytes contains the required number of * bytes and will be bigger that the original value. @@ -3217,7 +3217,7 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given * by the PNG_OPTION_ defines below. * - * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions, + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, * are detected at run time, however sometimes it may be impossible * to do this in user mode, in which case it is necessary to discover * the capabilities in an OS specific way. Such capabilities are diff --git a/thirdparty/libpng/pngconf.h b/thirdparty/libpng/pngconf.h index d13b13e57a..a4646bab85 100644 --- a/thirdparty/libpng/pngconf.h +++ b/thirdparty/libpng/pngconf.h @@ -1,9 +1,9 @@ /* pngconf.h - machine configurable file for libpng * - * libpng version 1.6.34, September 29, 2017 + * libpng version 1.6.35, July 15, 2018 * - * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -127,7 +127,7 @@ * * These cases only differ if the operating system does not use the C * calling convention, at present this just means the above cases - * (x86 DOS/Windows sytems) and, even then, this does not apply to + * (x86 DOS/Windows systems) and, even then, this does not apply to * Cygwin running on those systems. * * Note that the value must be defined in pnglibconf.h so that what @@ -515,8 +515,10 @@ # error "libpng requires an unsigned 32-bit (or more) type" #endif -/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however, - * requires an ISOC90 compiler and relies on consistent behavior of sizeof. +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. */ typedef size_t png_size_t; typedef ptrdiff_t png_ptrdiff_t; @@ -537,13 +539,12 @@ typedef ptrdiff_t png_ptrdiff_t; # endif #endif -/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no - * smaller than png_uint_32. Casts from png_size_t or png_uint_32 to - * png_alloc_size_t are not necessary; in fact, it is recommended not to use - * them at all so that the compiler can complain when something turns out to be - * problematic. +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. * - * Casts in the other direction (from png_alloc_size_t to png_size_t or + * Casts in the other direction (from png_alloc_size_t to size_t or * png_uint_32) should be explicitly applied; however, we do not expect to * encounter practical situations that require such conversions. * @@ -553,7 +554,7 @@ typedef ptrdiff_t png_ptrdiff_t; #ifdef PNG_SMALL_SIZE_T typedef png_uint_32 png_alloc_size_t; #else - typedef png_size_t png_alloc_size_t; + typedef size_t png_alloc_size_t; #endif /* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler @@ -589,8 +590,8 @@ typedef char * png_charp; typedef const char * png_const_charp; typedef png_fixed_point * png_fixed_point_p; typedef const png_fixed_point * png_const_fixed_point_p; -typedef png_size_t * png_size_tp; -typedef const png_size_t * png_const_size_tp; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; #ifdef PNG_STDIO_SUPPORTED typedef FILE * png_FILE_p; diff --git a/thirdparty/libpng/pngget.c b/thirdparty/libpng/pngget.c index 26e9fb1c35..2325508f1d 100644 --- a/thirdparty/libpng/pngget.c +++ b/thirdparty/libpng/pngget.c @@ -1,8 +1,8 @@ /* pngget.c - retrieval of values from info struct * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -26,7 +26,7 @@ png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, return(0); } -png_size_t PNGAPI +size_t PNGAPI png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) @@ -367,7 +367,7 @@ png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) static png_fixed_point png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) { - /* Convert from metres * 1,000,000 to inches * 100,000, meters to + /* Convert from meters * 1,000,000 to inches * 100,000, meters to * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127. * Notice that this can overflow - a warning is output and 0 is * returned. @@ -741,8 +741,7 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) != 0 && - name != NULL && compression_type != NULL && profile != NULL && - proflen != NULL) + name != NULL && profile != NULL && proflen != NULL) { *name = info_ptr->iccp_name; *profile = info_ptr->iccp_profile; @@ -750,11 +749,13 @@ png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, /* This is somewhat irrelevant since the profile data returned has * actually been uncompressed. */ - *compression_type = PNG_COMPRESSION_TYPE_BASE; + if (compression_type != NULL) + *compression_type = PNG_COMPRESSION_TYPE_BASE; return (PNG_INFO_iCCP); } return (0); + } #endif @@ -1164,7 +1165,7 @@ png_get_user_chunk_ptr(png_const_structrp png_ptr) } #endif -png_size_t PNGAPI +size_t PNGAPI png_get_compression_buffer_size(png_const_structrp png_ptr) { if (png_ptr == NULL) diff --git a/thirdparty/libpng/pnginfo.h b/thirdparty/libpng/pnginfo.h index d5f6149dbd..2fcf868dac 100644 --- a/thirdparty/libpng/pnginfo.h +++ b/thirdparty/libpng/pnginfo.h @@ -1,8 +1,8 @@ /* pnginfo.h - header file for PNG reference library * - * Last changed in libpng 1.6.1 [March 28, 2013] - * Copyright (c) 1998-2002,2004,2006-2013 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2013,2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -55,10 +55,10 @@ struct png_info_def { /* The following are necessary for every PNG file */ - png_uint_32 width; /* width of image in pixels (from IHDR) */ - png_uint_32 height; /* height of image in pixels (from IHDR) */ - png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ - png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + size_t rowbytes; /* bytes needed to hold an untransformed row */ png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ @@ -247,7 +247,7 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) /* The sCAL chunk describes the actual physical dimensions of the * subject matter of the graphic. The chunk contains a unit specification * a byte value, and two ASCII strings representing floating-point - * values. The values are width and height corresponsing to one pixel + * values. The values are width and height corresponding to one pixel * in the image. Data values are valid if (valid & PNG_INFO_sCAL) is * non-zero. */ diff --git a/thirdparty/libpng/pnglibconf.h b/thirdparty/libpng/pnglibconf.h index 53b5e442c4..00acecc69b 100644 --- a/thirdparty/libpng/pnglibconf.h +++ b/thirdparty/libpng/pnglibconf.h @@ -1,10 +1,10 @@ -/* libpng 1.6.34 STANDARD API DEFINITION */ +/* libpng 1.6.35 STANDARD API DEFINITION */ /* pnglibconf.h - library build configuration */ -/* Libpng version 1.6.34 - September 29, 2017 */ +/* Libpng version 1.6.35 - July 15, 2018 */ -/* Copyright (c) 1998-2017 Glenn Randers-Pehrson */ +/* Copyright (c) 1998-2018 Glenn Randers-Pehrson */ /* This code is released under the libpng license. */ /* For conditions of distribution and use, see the disclaimer */ diff --git a/thirdparty/libpng/pngpread.c b/thirdparty/libpng/pngpread.c index fbe361dc34..c4ba51c4d4 100644 --- a/thirdparty/libpng/pngpread.c +++ b/thirdparty/libpng/pngpread.c @@ -1,8 +1,8 @@ /* pngpread.c - read a png file in push mode * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -34,7 +34,7 @@ if (png_ptr->buffer_size < N) \ void PNGAPI png_process_data(png_structrp png_ptr, png_inforp info_ptr, - png_bytep buffer, png_size_t buffer_size) + png_bytep buffer, size_t buffer_size) { if (png_ptr == NULL || info_ptr == NULL) return; @@ -47,7 +47,7 @@ png_process_data(png_structrp png_ptr, png_inforp info_ptr, } } -png_size_t PNGAPI +size_t PNGAPI png_process_data_pause(png_structrp png_ptr, int save) { if (png_ptr != NULL) @@ -60,7 +60,7 @@ png_process_data_pause(png_structrp png_ptr, int save) else { /* This includes any pending saved bytes: */ - png_size_t remaining = png_ptr->buffer_size; + size_t remaining = png_ptr->buffer_size; png_ptr->buffer_size = 0; /* So subtract the saved buffer size, unless all the data @@ -133,8 +133,8 @@ png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) void /* PRIVATE */ png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) { - png_size_t num_checked = png_ptr->sig_bytes, /* SAFE, does not exceed 8 */ - num_to_check = 8 - num_checked; + size_t num_checked = png_ptr->sig_bytes; /* SAFE, does not exceed 8 */ + size_t num_to_check = 8 - num_checked; if (png_ptr->buffer_size < num_to_check) { @@ -418,7 +418,7 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) } void PNGCBAPI -png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, size_t length) { png_bytep ptr; @@ -428,7 +428,7 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) ptr = buffer; if (png_ptr->save_buffer_size != 0) { - png_size_t save_size; + size_t save_size; if (length < png_ptr->save_buffer_size) save_size = length; @@ -445,7 +445,7 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) } if (length != 0 && png_ptr->current_buffer_size != 0) { - png_size_t save_size; + size_t save_size; if (length < png_ptr->current_buffer_size) save_size = length; @@ -467,7 +467,7 @@ png_push_save_buffer(png_structrp png_ptr) { if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) { - png_size_t i, istop; + size_t i, istop; png_bytep sp; png_bytep dp; @@ -482,7 +482,7 @@ png_push_save_buffer(png_structrp png_ptr) if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > png_ptr->save_buffer_max) { - png_size_t new_max; + size_t new_max; png_bytep old_buffer; if (png_ptr->save_buffer_size > PNG_SIZE_MAX - @@ -494,7 +494,7 @@ png_push_save_buffer(png_structrp png_ptr) new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; old_buffer = png_ptr->save_buffer; png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, - (png_size_t)new_max); + (size_t)new_max); if (png_ptr->save_buffer == NULL) { @@ -522,7 +522,7 @@ png_push_save_buffer(png_structrp png_ptr) void /* PRIVATE */ png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, - png_size_t buffer_length) + size_t buffer_length) { png_ptr->current_buffer = buffer; png_ptr->current_buffer_size = buffer_length; @@ -562,7 +562,7 @@ png_push_read_IDAT(png_structrp png_ptr) if (png_ptr->idat_size != 0 && png_ptr->save_buffer_size != 0) { - png_size_t save_size = png_ptr->save_buffer_size; + size_t save_size = png_ptr->save_buffer_size; png_uint_32 idat_size = png_ptr->idat_size; /* We want the smaller of 'idat_size' and 'current_buffer_size', but they @@ -572,7 +572,7 @@ png_push_read_IDAT(png_structrp png_ptr) * will break on either 16-bit or 64-bit platforms. */ if (idat_size < save_size) - save_size = (png_size_t)idat_size; + save_size = (size_t)idat_size; else idat_size = (png_uint_32)save_size; @@ -589,7 +589,7 @@ png_push_read_IDAT(png_structrp png_ptr) if (png_ptr->idat_size != 0 && png_ptr->current_buffer_size != 0) { - png_size_t save_size = png_ptr->current_buffer_size; + size_t save_size = png_ptr->current_buffer_size; png_uint_32 idat_size = png_ptr->idat_size; /* We want the smaller of 'idat_size' and 'current_buffer_size', but they @@ -598,7 +598,7 @@ png_push_read_IDAT(png_structrp png_ptr) * larger - this cannot overflow. */ if (idat_size < save_size) - save_size = (png_size_t)idat_size; + save_size = (size_t)idat_size; else idat_size = (png_uint_32)save_size; @@ -625,7 +625,7 @@ png_push_read_IDAT(png_structrp png_ptr) void /* PRIVATE */ png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, - png_size_t buffer_length) + size_t buffer_length) { /* The caller checks for a non-zero buffer length. */ if (!(buffer_length > 0) || buffer == NULL) diff --git a/thirdparty/libpng/pngpriv.h b/thirdparty/libpng/pngpriv.h index 1f2e90f2b3..3581f67919 100644 --- a/thirdparty/libpng/pngpriv.h +++ b/thirdparty/libpng/pngpriv.h @@ -1,8 +1,8 @@ /* pngpriv.h - private declarations for use inside libpng * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -210,7 +210,11 @@ defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || \ (defined(_M_IX86_FP) && _M_IX86_FP >= 2) # define PNG_INTEL_SSE_OPT 1 +# else +# define PNG_INTEL_SSE_OPT 0 # endif +# else +# define PNG_INTEL_SSE_OPT 0 # endif #endif @@ -234,6 +238,8 @@ # if PNG_INTEL_SSE_IMPLEMENTATION > 0 # define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_sse2 # endif +#else +# define PNG_INTEL_SSE_IMPLEMENTATION 0 #endif #if PNG_MIPS_MSA_OPT > 0 @@ -728,8 +734,8 @@ /* Added to libpng-1.2.6 JB */ #define PNG_ROWBYTES(pixel_bits, width) \ ((pixel_bits) >= 8 ? \ - ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ - (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + ((size_t)(width) * (((size_t)(pixel_bits)) >> 3)) : \ + (( ((size_t)(width) * ((size_t)(pixel_bits))) + 7) >> 3) ) /* This returns the number of trailing bits in the last byte of a row, 0 if the * last byte is completely full of pixels. It is, in principle, (pixel_bits x @@ -917,7 +923,7 @@ * PNG files the -I directives must match. * * The most likely explanation is that you passed a -I in CFLAGS. This will - * not work; all the preprocessor directories and in particular all the -I + * not work; all the preprocessor directives and in particular all the -I * directives must be in CPPFLAGS. */ #endif @@ -1046,15 +1052,15 @@ PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); */ PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, - png_bytep data, png_size_t length),PNG_EMPTY); + png_bytep data, size_t length),PNG_EMPTY); #ifdef PNG_PROGRESSIVE_READ_SUPPORTED PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, - png_bytep buffer, png_size_t length),PNG_EMPTY); + png_bytep buffer, size_t length),PNG_EMPTY); #endif PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, - png_bytep data, png_size_t length),PNG_EMPTY); + png_bytep data, size_t length),PNG_EMPTY); #ifdef PNG_WRITE_FLUSH_SUPPORTED # ifdef PNG_STDIO_SUPPORTED @@ -1068,7 +1074,7 @@ PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); /* Write the "data" buffer to whatever output you are using */ PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, - png_const_bytep data, png_size_t length),PNG_EMPTY); + png_const_bytep data, size_t length),PNG_EMPTY); /* Read and check the PNG file signature */ PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, @@ -1080,7 +1086,7 @@ PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), /* Read data from whatever input you are using into the "data" buffer */ PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, - png_size_t length),PNG_EMPTY); + size_t length),PNG_EMPTY); /* Read bytes into buf, and update png_ptr->crc */ PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, @@ -1098,7 +1104,7 @@ PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); * since this is the maximum buffer size we can specify. */ PNG_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr, - png_const_bytep ptr, png_size_t length),PNG_EMPTY); + png_const_bytep ptr, size_t length),PNG_EMPTY); #ifdef PNG_WRITE_FLUSH_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); @@ -1181,7 +1187,7 @@ PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, /* Chunks that have keywords */ #ifdef PNG_WRITE_tEXt_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, - png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); + png_const_charp key, png_const_charp text, size_t text_len),PNG_EMPTY); #endif #ifdef PNG_WRITE_zTXt_SUPPORTED @@ -1574,10 +1580,10 @@ PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); + png_bytep buffer, size_t buffer_length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, - png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); + png_bytep buffer, size_t buffer_length),PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), PNG_EMPTY); PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, @@ -1847,13 +1853,13 @@ PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, #ifdef PNG_FLOATING_POINT_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr, - png_charp ascii, png_size_t size, double fp, unsigned int precision), + png_charp ascii, size_t size, double fp, unsigned int precision), PNG_EMPTY); #endif /* FLOATING_POINT */ #ifdef PNG_FIXED_POINT_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, - png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY); + png_charp ascii, size_t size, png_fixed_point fp),PNG_EMPTY); #endif /* FIXED_POINT */ #endif /* sCAL */ @@ -1946,7 +1952,7 @@ PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, * the problem character.) This has not been tested within libpng. */ PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, - png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); + size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); /* This is the same but it checks a complete string and returns true * only if it just contains a floating point number. As of 1.5.4 this @@ -1955,7 +1961,7 @@ PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, * for negative or zero values using the sticky flag. */ PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, - png_size_t size),PNG_EMPTY); + size_t size),PNG_EMPTY); #endif /* pCAL || sCAL */ #if defined(PNG_GAMMA_SUPPORTED) ||\ @@ -2030,7 +2036,7 @@ typedef struct png_control png_voidp error_buf; /* Always a jmp_buf at present. */ png_const_bytep memory; /* Memory buffer. */ - png_size_t size; /* Size of the memory buffer. */ + size_t size; /* Size of the memory buffer. */ unsigned int for_write :1; /* Otherwise it is a read structure */ unsigned int owned_file :1; /* We own the file in io_ptr */ diff --git a/thirdparty/libpng/pngread.c b/thirdparty/libpng/pngread.c index da32e9ad9c..bff7503ee3 100644 --- a/thirdparty/libpng/pngread.c +++ b/thirdparty/libpng/pngread.c @@ -1,8 +1,8 @@ /* pngread.c - read a PNG file * - * Last changed in libpng 1.6.33 [September 28, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -1532,7 +1532,7 @@ png_image_begin_read_from_file(png_imagep image, const char *file_name) #endif /* STDIO */ static void PNGCBAPI -png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) +png_image_memory_read(png_structp png_ptr, png_bytep out, size_t need) { if (png_ptr != NULL) { @@ -1543,7 +1543,7 @@ png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) if (cp != NULL) { png_const_bytep memory = cp->memory; - png_size_t size = cp->size; + size_t size = cp->size; if (memory != NULL && size >= need) { @@ -1562,7 +1562,7 @@ png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) } int PNGAPI png_image_begin_read_from_memory(png_imagep image, - png_const_voidp memory, png_size_t size) + png_const_voidp memory, size_t size) { if (image != NULL && image->version == PNG_IMAGE_VERSION) { @@ -4150,7 +4150,7 @@ png_image_finish_read(png_imagep image, png_const_colorp background, * * NOTE: this will be changed in 1.7 because PNG_IMAGE_BUFFER_SIZE * will be changed to use png_alloc_size_t; bigger images can be - * accomodated on 64-bit systems. + * accommodated on 64-bit systems. */ if (image->height <= 0xffffffffU/PNG_IMAGE_PIXEL_COMPONENT_SIZE(image->format)/check) diff --git a/thirdparty/libpng/pngrio.c b/thirdparty/libpng/pngrio.c index 7e26e855ca..372221483f 100644 --- a/thirdparty/libpng/pngrio.c +++ b/thirdparty/libpng/pngrio.c @@ -1,8 +1,8 @@ /* pngrio.c - functions for data input * - * Last changed in libpng 1.6.24 [August 4, 2016] - * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -29,7 +29,7 @@ * to read more than 64K on a 16-bit machine. */ void /* PRIVATE */ -png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) +png_read_data(png_structrp png_ptr, png_bytep data, size_t length) { png_debug1(4, "reading %d bytes", (int)length); @@ -47,14 +47,14 @@ png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) * than changing the library. */ void PNGCBAPI -png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +png_default_read_data(png_structp png_ptr, png_bytep data, size_t length) { - png_size_t check; + size_t check; if (png_ptr == NULL) return; - /* fread() returns 0 on error, so it is OK to store this in a png_size_t + /* fread() returns 0 on error, so it is OK to store this in a size_t * instead of an int, which is what fread() actually returns. */ check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); diff --git a/thirdparty/libpng/pngrtran.c b/thirdparty/libpng/pngrtran.c index c189650313..67d1f249a6 100644 --- a/thirdparty/libpng/pngrtran.c +++ b/thirdparty/libpng/pngrtran.c @@ -1,8 +1,8 @@ /* pngrtran.c - transforms the data in a row for PNG readers * - * Last changed in libpng 1.6.33 [September 28, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -292,7 +292,7 @@ png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, * who use the inverse of the gamma value accidentally! Since some of these * values are reasonable this may have to be changed: * - * 1.6.x: changed from 0.07..3 to 0.01..100 (to accomodate the optimal 16-bit + * 1.6.x: changed from 0.07..3 to 0.01..100 (to accommodate the optimal 16-bit * gamma of 36, and its reciprocal.) */ if (output_gamma < 1000 || output_gamma > 10000000) @@ -747,7 +747,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, int num_red = (1 << PNG_QUANTIZE_RED_BITS); int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); - png_size_t num_entries = ((png_size_t)1 << total_bits); + size_t num_entries = ((size_t)1 << total_bits); png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, (png_alloc_size_t)(num_entries * (sizeof (png_byte)))); @@ -1317,7 +1317,7 @@ png_init_read_transformations(png_structrp png_ptr) else if (png_ptr->screen_gamma != 0) /* The converse - assume the file matches the screen, note that this - * perhaps undesireable default can (from 1.5.4) be changed by calling + * perhaps undesirable default can (from 1.5.4) be changed by calling * png_set_alpha_mode (even if the alpha handling mode isn't required * or isn't changed from the default.) */ @@ -1885,7 +1885,7 @@ png_init_read_transformations(png_structrp png_ptr) png_ptr->transformations &= ~PNG_SHIFT; - /* significant bits can be in the range 1 to 7 for a meaninful result, if + /* significant bits can be in the range 1 to 7 for a meaningful result, if * the number of significant bits is 0 then no shift is done (this is an * error condition which is silently ignored.) */ @@ -2151,8 +2151,8 @@ png_do_unpack(png_row_infop row_info, png_bytep row) { case 1: { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); - png_bytep dp = row + (png_size_t)row_width - 1; + png_bytep sp = row + (size_t)((row_width - 1) >> 3); + png_bytep dp = row + (size_t)row_width - 1; png_uint_32 shift = 7U - ((row_width + 7U) & 0x07); for (i = 0; i < row_width; i++) { @@ -2175,8 +2175,8 @@ png_do_unpack(png_row_infop row_info, png_bytep row) case 2: { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); - png_bytep dp = row + (png_size_t)row_width - 1; + png_bytep sp = row + (size_t)((row_width - 1) >> 2); + png_bytep dp = row + (size_t)row_width - 1; png_uint_32 shift = ((3U - ((row_width + 3U) & 0x03)) << 1); for (i = 0; i < row_width; i++) { @@ -2198,8 +2198,8 @@ png_do_unpack(png_row_infop row_info, png_bytep row) case 4: { - png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); - png_bytep dp = row + (png_size_t)row_width - 1; + png_bytep sp = row + (size_t)((row_width - 1) >> 1); + png_bytep dp = row + (size_t)row_width - 1; png_uint_32 shift = ((1U - ((row_width + 1U) & 0x01)) << 2); for (i = 0; i < row_width; i++) { @@ -2463,95 +2463,94 @@ png_do_chop(png_row_infop row_info, png_bytep row) static void png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) { + png_uint_32 row_width = row_info->width; + png_debug(1, "in png_do_read_swap_alpha"); + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_uint_32 row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) { - /* This converts from RGBA to ARGB */ - if (row_info->bit_depth == 8) - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; - } + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; } + } #ifdef PNG_READ_16BIT_SUPPORTED - /* This converts from RRGGBBAA to AARRGGBB */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; - } + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; } -#endif } +#endif + } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) { - /* This converts from GA to AG */ - if (row_info->bit_depth == 8) - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save; - png_uint_32 i; + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - save = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save; - } + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; } + } #ifdef PNG_READ_16BIT_SUPPORTED - /* This converts from GGAA to AAGG */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_byte save[2]; - png_uint_32 i; + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - save[0] = *(--sp); - save[1] = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = save[0]; - *(--dp) = save[1]; - } + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; } -#endif } +#endif } } #endif @@ -2681,8 +2680,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, if ((flags & PNG_FLAG_FILLER_AFTER) != 0) { /* This changes the data from G to GX */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; + png_bytep sp = row + (size_t)row_width; + png_bytep dp = sp + (size_t)row_width; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; @@ -2697,8 +2696,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, else { /* This changes the data from G to XG */ - png_bytep sp = row + (png_size_t)row_width; - png_bytep dp = sp + (png_size_t)row_width; + png_bytep sp = row + (size_t)row_width; + png_bytep dp = sp + (size_t)row_width; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); @@ -2716,8 +2715,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, if ((flags & PNG_FLAG_FILLER_AFTER) != 0) { /* This changes the data from GG to GGXX */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width * 2; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; @@ -2735,8 +2734,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, else { /* This changes the data from GG to XXGG */ - png_bytep sp = row + (png_size_t)row_width * 2; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width * 2; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); @@ -2758,8 +2757,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, if ((flags & PNG_FLAG_FILLER_AFTER) != 0) { /* This changes the data from RGB to RGBX */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; + png_bytep sp = row + (size_t)row_width * 3; + png_bytep dp = sp + (size_t)row_width; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; @@ -2776,8 +2775,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, else { /* This changes the data from RGB to XRGB */ - png_bytep sp = row + (png_size_t)row_width * 3; - png_bytep dp = sp + (png_size_t)row_width; + png_bytep sp = row + (size_t)row_width * 3; + png_bytep dp = sp + (size_t)row_width; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); @@ -2797,8 +2796,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, if ((flags & PNG_FLAG_FILLER_AFTER) != 0) { /* This changes the data from RRGGBB to RRGGBBXX */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width * 6; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 1; i < row_width; i++) { *(--dp) = lo_filler; @@ -2820,8 +2819,8 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, else { /* This changes the data from RRGGBB to XXRRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 6; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width * 6; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(--dp) = *(--sp); @@ -2862,8 +2861,8 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) if (row_info->bit_depth == 8) { /* This changes G to RGB */ - png_bytep sp = row + (png_size_t)row_width - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width - 1; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(dp--) = *sp; @@ -2875,8 +2874,8 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) else { /* This changes GG to RRGGBB */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; + png_bytep sp = row + (size_t)row_width * 2 - 1; + png_bytep dp = sp + (size_t)row_width * 4; for (i = 0; i < row_width; i++) { *(dp--) = *sp; @@ -2894,8 +2893,8 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) if (row_info->bit_depth == 8) { /* This changes GA to RGBA */ - png_bytep sp = row + (png_size_t)row_width * 2 - 1; - png_bytep dp = sp + (png_size_t)row_width * 2; + png_bytep sp = row + (size_t)row_width * 2 - 1; + png_bytep dp = sp + (size_t)row_width * 2; for (i = 0; i < row_width; i++) { *(dp--) = *(sp--); @@ -2908,8 +2907,8 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) else { /* This changes GGAA to RRGGBBAA */ - png_bytep sp = row + (png_size_t)row_width * 4 - 1; - png_bytep dp = sp + (png_size_t)row_width * 4; + png_bytep sp = row + (size_t)row_width * 4 - 1; + png_bytep dp = sp + (size_t)row_width * 4; for (i = 0; i < row_width; i++) { *(dp--) = *(sp--); @@ -2980,7 +2979,7 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) * values this results in an implicit assumption that the original PNG RGB * values were linear. * - * Other integer coefficents can be used via png_set_rgb_to_gray(). Because + * Other integer coefficients can be used via png_set_rgb_to_gray(). Because * the API takes just red and green coefficients the blue coefficient is * calculated to make the sum 32768. This will result in different rounding * to that used above. @@ -3209,720 +3208,718 @@ png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) png_debug(1, "in png_do_compose"); + switch (row_info->color_type) { - switch (row_info->color_type) + case PNG_COLOR_TYPE_GRAY: { - case PNG_COLOR_TYPE_GRAY: + switch (row_info->bit_depth) { - switch (row_info->bit_depth) + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == png_ptr->trans_color.gray) + { + unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); + } + + if (shift == 0) + { + shift = 7; + sp++; + } + + else + shift--; + } + break; + } + + case 2: { - case 1: +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) { sp = row; - shift = 7; + shift = 6; for (i = 0; i < row_width; i++) { - if ((png_uint_16)((*sp >> shift) & 0x01) - == png_ptr->trans_color.gray) + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) { - unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); tmp |= - (unsigned int)(png_ptr->background.gray << shift); + (unsigned int)png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); + } + + else + { + unsigned int p = (*sp >> shift) & 0x03; + unsigned int g = (gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03; + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= (unsigned int)(g << shift); *sp = (png_byte)(tmp & 0xff); } if (shift == 0) { - shift = 7; + shift = 6; sp++; } else - shift--; + shift -= 2; } - break; } - case 2: + else +#endif { -#ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_table != NULL) + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) { - sp = row; - shift = 6; - for (i = 0; i < row_width; i++) + if ((png_uint_16)((*sp >> shift) & 0x03) + == png_ptr->trans_color.gray) { - if ((png_uint_16)((*sp >> shift) & 0x03) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= - (unsigned int)png_ptr->background.gray << shift; - *sp = (png_byte)(tmp & 0xff); - } - - else - { - unsigned int p = (*sp >> shift) & 0x03; - unsigned int g = (gamma_table [p | (p << 2) | - (p << 4) | (p << 6)] >> 6) & 0x03; - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= (unsigned int)(g << shift); - *sp = (png_byte)(tmp & 0xff); - } - - if (shift == 0) - { - shift = 6; - sp++; - } - - else - shift -= 2; + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= + (unsigned int)png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } - } - else -#endif - { - sp = row; - shift = 6; - for (i = 0; i < row_width; i++) + if (shift == 0) { - if ((png_uint_16)((*sp >> shift) & 0x03) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); - tmp |= - (unsigned int)png_ptr->background.gray << shift; - *sp = (png_byte)(tmp & 0xff); - } - - if (shift == 0) - { - shift = 6; - sp++; - } - - else - shift -= 2; + shift = 6; + sp++; } + + else + shift -= 2; } - break; } + break; + } - case 4: - { + case 4: + { #ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_table != NULL) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) { - sp = row; - shift = 4; - for (i = 0; i < row_width; i++) + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) { - if ((png_uint_16)((*sp >> shift) & 0x0f) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); - tmp |= - (unsigned int)(png_ptr->background.gray << shift); - *sp = (png_byte)(tmp & 0xff); - } - - else - { - unsigned int p = (*sp >> shift) & 0x0f; - unsigned int g = (gamma_table[p | (p << 4)] >> 4) & - 0x0f; - unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); - tmp |= (unsigned int)(g << shift); - *sp = (png_byte)(tmp & 0xff); - } - - if (shift == 0) - { - shift = 4; - sp++; - } - - else - shift -= 4; + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); } - } - else -#endif - { - sp = row; - shift = 4; - for (i = 0; i < row_width; i++) + else { - if ((png_uint_16)((*sp >> shift) & 0x0f) - == png_ptr->trans_color.gray) - { - unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); - tmp |= - (unsigned int)(png_ptr->background.gray << shift); - *sp = (png_byte)(tmp & 0xff); - } - - if (shift == 0) - { - shift = 4; - sp++; - } - - else - shift -= 4; + unsigned int p = (*sp >> shift) & 0x0f; + unsigned int g = (gamma_table[p | (p << 4)] >> 4) & + 0x0f; + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= (unsigned int)(g << shift); + *sp = (png_byte)(tmp & 0xff); } - } - break; - } - case 8: - { -#ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_table != NULL) - { - sp = row; - for (i = 0; i < row_width; i++, sp++) - { - if (*sp == png_ptr->trans_color.gray) - *sp = (png_byte)png_ptr->background.gray; - - else - *sp = gamma_table[*sp]; - } - } - else -#endif - { - sp = row; - for (i = 0; i < row_width; i++, sp++) + if (shift == 0) { - if (*sp == png_ptr->trans_color.gray) - *sp = (png_byte)png_ptr->background.gray; + shift = 4; + sp++; } + + else + shift -= 4; } - break; } - case 16: + else +#endif { -#ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_16 != NULL) + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) + if ((png_uint_16)((*sp >> shift) & 0x0f) + == png_ptr->trans_color.gray) { - png_uint_16 v; - - v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - if (v == png_ptr->trans_color.gray) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray - & 0xff); - } - - else - { - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } + unsigned int tmp = *sp & (0x0f0f >> (4 - shift)); + tmp |= + (unsigned int)(png_ptr->background.gray << shift); + *sp = (png_byte)(tmp & 0xff); } - } - else -#endif - { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) - { - png_uint_16 v; - v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - if (v == png_ptr->trans_color.gray) - { - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray - & 0xff); - } + if (shift == 0) + { + shift = 4; + sp++; } + + else + shift -= 4; } - break; } - - default: - break; + break; } - break; - } - case PNG_COLOR_TYPE_RGB: - { - if (row_info->bit_depth == 8) + case 8: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; - for (i = 0; i < row_width; i++, sp += 3) + for (i = 0; i < row_width; i++, sp++) { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(sp + 2) == png_ptr->trans_color.blue) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; else - { *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } } } else #endif { sp = row; - for (i = 0; i < row_width; i++, sp += 3) + for (i = 0; i < row_width; i++, sp++) { - if (*sp == png_ptr->trans_color.red && - *(sp + 1) == png_ptr->trans_color.green && - *(sp + 2) == png_ptr->trans_color.blue) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; } } + break; } - else /* if (row_info->bit_depth == 16) */ + + case 16: { #ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; - for (i = 0; i < row_width; i++, sp += 6) + for (i = 0; i < row_width; i++, sp += 2) { - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); + png_uint_16 v; - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - if (r == png_ptr->trans_color.red && - g == png_ptr->trans_color.green && - b == png_ptr->trans_color.blue) + if (v == png_ptr->trans_color.gray) { /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); } else { - png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); - - v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); - - v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); } } } - else #endif { sp = row; - for (i = 0; i < row_width; i++, sp += 6) + for (i = 0; i < row_width; i++, sp += 2) { - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); + png_uint_16 v; - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - if (r == png_ptr->trans_color.red && - g == png_ptr->trans_color.green && - b == png_ptr->trans_color.blue) + if (v == png_ptr->trans_color.gray) { - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); } } } + break; } - break; + + default: + break; } + break; + } - case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) - { #ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) { - png_uint_16 a = *(sp + 1); + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } - if (a == 0xff) - *sp = gamma_table[*sp]; + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)png_ptr->background.gray; - } + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); - else - { - png_byte v, w; + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); - v = gamma_to_1[*sp]; - png_composite(w, v, a, png_ptr->background_1.gray); - if (optimize == 0) - w = gamma_from_1[w]; - *sp = w; - } + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); } } - else + } + + else #endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) { - sp = row; - for (i = 0; i < row_width; i++, sp += 2) + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) { - png_byte a = *(sp + 1); + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } + } + } + } + break; + } - if (a == 0) - *sp = (png_byte)png_ptr->background.gray; + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + *sp = gamma_table[*sp]; + + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.gray; + } + + else + { + png_byte v, w; - else if (a < 0xff) - png_composite(*sp, *sp, a, png_ptr->background.gray); + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.gray); + if (optimize == 0) + w = gamma_from_1[w]; + *sp = w; } } } - else /* if (png_ptr->bit_depth == 16) */ + else +#endif { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_byte a = *(sp + 1); + + if (a == 0) + *sp = (png_byte)png_ptr->background.gray; + + else if (a < 0xff) + png_composite(*sp, *sp, a, png_ptr->background.gray); + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { #ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == (png_uint_16)0xffff) { - png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); + png_uint_16 v; - if (a == (png_uint_16)0xffff) - { - png_uint_16 v; + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); - } + else + { + png_uint_16 g, v, w; + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, png_ptr->background_1.gray); + if (optimize != 0) + w = v; else - { - png_uint_16 g, v, w; - - g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; - png_composite_16(v, g, a, png_ptr->background_1.gray); - if (optimize != 0) - w = v; - else - w = gamma_16_from_1[(v & 0xff) >> - gamma_shift][v >> 8]; - *sp = (png_byte)((w >> 8) & 0xff); - *(sp + 1) = (png_byte)(w & 0xff); - } + w = gamma_16_from_1[(v & 0xff) >> + gamma_shift][v >> 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); } } - else + } + else #endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); - if (a == 0) - { - *sp = (png_byte)((png_ptr->background.gray >> 8) - & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); - } + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); + } - else if (a < 0xffff) - { - png_uint_16 g, v; + else if (a < 0xffff) + { + png_uint_16 g, v; - g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_composite_16(v, g, a, png_ptr->background.gray); - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - } + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, png_ptr->background.gray); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); } } } - break; } + break; + } - case PNG_COLOR_TYPE_RGB_ALPHA: + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) - { #ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_to_1 != NULL && gamma_from_1 != NULL && - gamma_table != NULL) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) + png_byte a = *(sp + 3); + + if (a == 0xff) { - png_byte a = *(sp + 3); + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } - if (a == 0xff) - { - *sp = gamma_table[*sp]; - *(sp + 1) = gamma_table[*(sp + 1)]; - *(sp + 2) = gamma_table[*(sp + 2)]; - } + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } + else + { + png_byte v, w; - else - { - png_byte v, w; - - v = gamma_to_1[*sp]; - png_composite(w, v, a, png_ptr->background_1.red); - if (optimize == 0) w = gamma_from_1[w]; - *sp = w; - - v = gamma_to_1[*(sp + 1)]; - png_composite(w, v, a, png_ptr->background_1.green); - if (optimize == 0) w = gamma_from_1[w]; - *(sp + 1) = w; - - v = gamma_to_1[*(sp + 2)]; - png_composite(w, v, a, png_ptr->background_1.blue); - if (optimize == 0) w = gamma_from_1[w]; - *(sp + 2) = w; - } + v = gamma_to_1[*sp]; + png_composite(w, v, a, png_ptr->background_1.red); + if (optimize == 0) w = gamma_from_1[w]; + *sp = w; + + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, png_ptr->background_1.green); + if (optimize == 0) w = gamma_from_1[w]; + *(sp + 1) = w; + + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, png_ptr->background_1.blue); + if (optimize == 0) w = gamma_from_1[w]; + *(sp + 2) = w; } } - else + } + else #endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 4) { - sp = row; - for (i = 0; i < row_width; i++, sp += 4) - { - png_byte a = *(sp + 3); + png_byte a = *(sp + 3); - if (a == 0) - { - *sp = (png_byte)png_ptr->background.red; - *(sp + 1) = (png_byte)png_ptr->background.green; - *(sp + 2) = (png_byte)png_ptr->background.blue; - } + if (a == 0) + { + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; + } - else if (a < 0xff) - { - png_composite(*sp, *sp, a, png_ptr->background.red); + else if (a < 0xff) + { + png_composite(*sp, *sp, a, png_ptr->background.red); - png_composite(*(sp + 1), *(sp + 1), a, - png_ptr->background.green); + png_composite(*(sp + 1), *(sp + 1), a, + png_ptr->background.green); - png_composite(*(sp + 2), *(sp + 2), a, - png_ptr->background.blue); - } + png_composite(*(sp + 2), *(sp + 2), a, + png_ptr->background.blue); } } } - else /* if (row_info->bit_depth == 16) */ - { + } + else /* if (row_info->bit_depth == 16) */ + { #ifdef PNG_READ_GAMMA_SUPPORTED - if (gamma_16 != NULL && gamma_16_from_1 != NULL && - gamma_16_to_1 != NULL) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) - << 8) + (png_uint_16)(*(sp + 7))); + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); - if (a == (png_uint_16)0xffff) - { - png_uint_16 v; + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; - v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); - v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); - } + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } - else if (a == 0) - { - /* Background is already in screen gamma */ - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } + else if (a == 0) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } - else - { - png_uint_16 v, w; - - v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; - png_composite_16(w, v, a, png_ptr->background_1.red); - if (optimize == 0) - w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> - 8]; - *sp = (png_byte)((w >> 8) & 0xff); - *(sp + 1) = (png_byte)(w & 0xff); - - v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; - png_composite_16(w, v, a, png_ptr->background_1.green); - if (optimize == 0) - w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> - 8]; - - *(sp + 2) = (png_byte)((w >> 8) & 0xff); - *(sp + 3) = (png_byte)(w & 0xff); - - v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; - png_composite_16(w, v, a, png_ptr->background_1.blue); - if (optimize == 0) - w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> - 8]; - - *(sp + 4) = (png_byte)((w >> 8) & 0xff); - *(sp + 5) = (png_byte)(w & 0xff); - } + else + { + png_uint_16 v, w; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, png_ptr->background_1.red); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, png_ptr->background_1.green); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 2) = (png_byte)((w >> 8) & 0xff); + *(sp + 3) = (png_byte)(w & 0xff); + + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, png_ptr->background_1.blue); + if (optimize == 0) + w = gamma_16_from_1[((w & 0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 4) = (png_byte)((w >> 8) & 0xff); + *(sp + 5) = (png_byte)(w & 0xff); } } + } - else + else #endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 8) { - sp = row; - for (i = 0; i < row_width; i++, sp += 8) - { - png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) - << 8) + (png_uint_16)(*(sp + 7))); + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); - if (a == 0) - { - *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); - *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); - *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) - & 0xff); - *(sp + 3) = (png_byte)(png_ptr->background.green - & 0xff); - *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) - & 0xff); - *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); - } + if (a == 0) + { + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); + } - else if (a < 0xffff) - { - png_uint_16 v; + else if (a < 0xffff) + { + png_uint_16 v; - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) - + *(sp + 3)); - png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) - + *(sp + 5)); + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); - png_composite_16(v, r, a, png_ptr->background.red); - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, r, a, png_ptr->background.red); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); - png_composite_16(v, g, a, png_ptr->background.green); - *(sp + 2) = (png_byte)((v >> 8) & 0xff); - *(sp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, png_ptr->background.green); + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); - png_composite_16(v, b, a, png_ptr->background.blue); - *(sp + 4) = (png_byte)((v >> 8) & 0xff); - *(sp + 5) = (png_byte)(v & 0xff); - } + png_composite_16(v, b, a, png_ptr->background.blue); + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); } } } - break; } - - default: - break; + break; } + + default: + break; } } #endif /* READ_BACKGROUND || READ_ALPHA_MODE */ @@ -4220,8 +4217,8 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, { case 1: { - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; + sp = row + (size_t)((row_width - 1) >> 3); + dp = row + (size_t)row_width - 1; shift = 7 - (int)((row_width + 7) & 0x07); for (i = 0; i < row_width; i++) { @@ -4247,8 +4244,8 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, case 2: { - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; + sp = row + (size_t)((row_width - 1) >> 2); + dp = row + (size_t)row_width - 1; shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); for (i = 0; i < row_width; i++) { @@ -4270,8 +4267,8 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, case 4: { - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; + sp = row + (size_t)((row_width - 1) >> 1); + dp = row + (size_t)row_width - 1; shift = (int)((row_width & 0x01) << 2); for (i = 0; i < row_width; i++) { @@ -4304,8 +4301,8 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, { if (num_trans > 0) { - sp = row + (png_size_t)row_width - 1; - dp = row + ((png_size_t)row_width << 2) - 1; + sp = row + (size_t)row_width - 1; + dp = row + ((size_t)row_width << 2) - 1; for (i = 0; i < row_width; i++) { @@ -4329,8 +4326,8 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, else { - sp = row + (png_size_t)row_width - 1; - dp = row + (png_size_t)(row_width * 3) - 1; + sp = row + (size_t)row_width - 1; + dp = row + (size_t)(row_width * 3) - 1; for (i = 0; i < row_width; i++) { @@ -4365,195 +4362,130 @@ png_do_expand(png_row_infop row_info, png_bytep row, png_debug(1, "in png_do_expand"); + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { - if (row_info->color_type == PNG_COLOR_TYPE_GRAY) - { - unsigned int gray = trans_color != NULL ? trans_color->gray : 0; + unsigned int gray = trans_color != NULL ? trans_color->gray : 0; - if (row_info->bit_depth < 8) + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) { - switch (row_info->bit_depth) + case 1: { - case 1: + gray = (gray & 0x01) * 0xff; + sp = row + (size_t)((row_width - 1) >> 3); + dp = row + (size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) { - gray = (gray & 0x01) * 0xff; - sp = row + (png_size_t)((row_width - 1) >> 3); - dp = row + (png_size_t)row_width - 1; - shift = 7 - (int)((row_width + 7) & 0x07); - for (i = 0; i < row_width; i++) - { - if ((*sp >> shift) & 0x01) - *dp = 0xff; - - else - *dp = 0; - - if (shift == 7) - { - shift = 0; - sp--; - } - - else - shift++; + if ((*sp >> shift) & 0x01) + *dp = 0xff; - dp--; - } - break; - } + else + *dp = 0; - case 2: - { - gray = (gray & 0x03) * 0x55; - sp = row + (png_size_t)((row_width - 1) >> 2); - dp = row + (png_size_t)row_width - 1; - shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); - for (i = 0; i < row_width; i++) + if (shift == 7) { - value = (*sp >> shift) & 0x03; - *dp = (png_byte)(value | (value << 2) | (value << 4) | - (value << 6)); - if (shift == 6) - { - shift = 0; - sp--; - } - - else - shift += 2; - - dp--; + shift = 0; + sp--; } - break; - } - - case 4: - { - gray = (gray & 0x0f) * 0x11; - sp = row + (png_size_t)((row_width - 1) >> 1); - dp = row + (png_size_t)row_width - 1; - shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); - for (i = 0; i < row_width; i++) - { - value = (*sp >> shift) & 0x0f; - *dp = (png_byte)(value | (value << 4)); - if (shift == 4) - { - shift = 0; - sp--; - } - else - shift = 4; + else + shift++; - dp--; - } - break; + dp--; } - - default: - break; + break; } - row_info->bit_depth = 8; - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; - } - - if (trans_color != NULL) - { - if (row_info->bit_depth == 8) + case 2: { - gray = gray & 0xff; - sp = row + (png_size_t)row_width - 1; - dp = row + ((png_size_t)row_width << 1) - 1; - + gray = (gray & 0x03) * 0x55; + sp = row + (size_t)((row_width - 1) >> 2); + dp = row + (size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); for (i = 0; i < row_width; i++) { - if ((*sp & 0xffU) == gray) - *dp-- = 0; + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } else - *dp-- = 0xff; + shift += 2; - *dp-- = *sp--; + dp--; } + break; } - else if (row_info->bit_depth == 16) + case 4: { - unsigned int gray_high = (gray >> 8) & 0xff; - unsigned int gray_low = gray & 0xff; - sp = row + row_info->rowbytes - 1; - dp = row + (row_info->rowbytes << 1) - 1; + gray = (gray & 0x0f) * 0x11; + sp = row + (size_t)((row_width - 1) >> 1); + dp = row + (size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); for (i = 0; i < row_width; i++) { - if ((*(sp - 1) & 0xffU) == gray_high && - (*(sp) & 0xffU) == gray_low) + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) { - *dp-- = 0; - *dp-- = 0; + shift = 0; + sp--; } else - { - *dp-- = 0xff; - *dp-- = 0xff; - } + shift = 4; - *dp-- = *sp--; - *dp-- = *sp--; + dp--; } + break; } - row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; - row_info->channels = 2; - row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_width); + default: + break; } + + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB && - trans_color != NULL) + + if (trans_color != NULL) { if (row_info->bit_depth == 8) { - png_byte red = (png_byte)(trans_color->red & 0xff); - png_byte green = (png_byte)(trans_color->green & 0xff); - png_byte blue = (png_byte)(trans_color->blue & 0xff); - sp = row + (png_size_t)row_info->rowbytes - 1; - dp = row + ((png_size_t)row_width << 2) - 1; + gray = gray & 0xff; + sp = row + (size_t)row_width - 1; + dp = row + ((size_t)row_width << 1) - 1; + for (i = 0; i < row_width; i++) { - if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + if ((*sp & 0xffU) == gray) *dp-- = 0; else *dp-- = 0xff; *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; } } + else if (row_info->bit_depth == 16) { - png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); - png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); - png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); - png_byte red_low = (png_byte)(trans_color->red & 0xff); - png_byte green_low = (png_byte)(trans_color->green & 0xff); - png_byte blue_low = (png_byte)(trans_color->blue & 0xff); + unsigned int gray_high = (gray >> 8) & 0xff; + unsigned int gray_low = gray & 0xff; sp = row + row_info->rowbytes - 1; - dp = row + ((png_size_t)row_width << 3) - 1; + dp = row + (row_info->rowbytes << 1) - 1; for (i = 0; i < row_width; i++) { - if (*(sp - 5) == red_high && - *(sp - 4) == red_low && - *(sp - 3) == green_high && - *(sp - 2) == green_low && - *(sp - 1) == blue_high && - *(sp ) == blue_low) + if ((*(sp - 1) & 0xffU) == gray_high && + (*(sp) & 0xffU) == gray_low) { *dp-- = 0; *dp-- = 0; @@ -4567,18 +4499,81 @@ png_do_expand(png_row_infop row_info, png_bytep row, *dp-- = *sp--; *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; - *dp-- = *sp--; } } - row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; - row_info->channels = 4; - row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); } } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && + trans_color != NULL) + { + if (row_info->bit_depth == 8) + { + png_byte red = (png_byte)(trans_color->red & 0xff); + png_byte green = (png_byte)(trans_color->green & 0xff); + png_byte blue = (png_byte)(trans_color->blue & 0xff); + sp = row + (size_t)row_info->rowbytes - 1; + dp = row + ((size_t)row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + + else + *dp-- = 0xff; + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); + png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); + png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); + png_byte red_low = (png_byte)(trans_color->red & 0xff); + png_byte green_low = (png_byte)(trans_color->green & 0xff); + png_byte blue_low = (png_byte)(trans_color->blue & 0xff); + sp = row + row_info->rowbytes - 1; + dp = row + ((size_t)row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } } #endif @@ -4760,8 +4755,7 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) &(png_ptr->trans_color)); else - png_do_expand(row_info, png_ptr->row_buf + 1, - NULL); + png_do_expand(row_info, png_ptr->row_buf + 1, NULL); } } #endif @@ -4985,7 +4979,7 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) (png_ptr, /* png_ptr */ row_info, /* row_info: */ /* png_uint_32 width; width of row */ - /* png_size_t rowbytes; number of bytes in row */ + /* size_t rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ /* png_byte bit_depth; bit depth of samples */ /* png_byte channels; number of channels (1-4) */ diff --git a/thirdparty/libpng/pngrutil.c b/thirdparty/libpng/pngrutil.c index 8692933bd8..7001f1976e 100644 --- a/thirdparty/libpng/pngrutil.c +++ b/thirdparty/libpng/pngrutil.c @@ -1,8 +1,8 @@ /* pngrutil.c - utilities to read a PNG file * - * Last changed in libpng 1.6.33 [September 28, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -102,7 +102,7 @@ png_get_int_32)(png_const_bytep buf) png_uint_16 (PNGAPI png_get_uint_16)(png_const_bytep buf) { - /* ANSI-C requires an int value to accomodate at least 16 bits so this + /* ANSI-C requires an int value to accommodate at least 16 bits so this * works and allows the compiler not to worry about possible narrowing * on 32-bit systems. (Pre-ANSI systems did not make integers smaller * than 16 bits either.) @@ -120,7 +120,7 @@ png_get_uint_16)(png_const_bytep buf) void /* PRIVATE */ png_read_sig(png_structrp png_ptr, png_inforp info_ptr) { - png_size_t num_checked, num_to_check; + size_t num_checked, num_to_check; /* Exit if the user application does not expect a signature. */ if (png_ptr->sig_bytes >= 8) @@ -1648,7 +1648,7 @@ png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) int entry_size, i; png_uint_32 skip = 0; png_uint_32 dl; - png_size_t max_dl; + size_t max_dl; png_debug(1, "in png_handle_sPLT"); @@ -1997,6 +1997,15 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else if ((png_ptr->color_type & PNG_COLOR_MASK_COLOR) == 0) /* GRAY */ { + if (png_ptr->bit_depth <= 8) + { + if (buf[0] != 0 || buf[1] >= (unsigned int)(1 << png_ptr->bit_depth)) + { + png_chunk_benign_error(png_ptr, "invalid gray level"); + return; + } + } + background.index = 0; background.red = background.green = @@ -2006,6 +2015,15 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { + if (png_ptr->bit_depth <= 8) + { + if (buf[0] != 0 || buf[2] != 0 || buf[4] != 0) + { + png_chunk_benign_error(png_ptr, "invalid color"); + return; + } + } + background.index = 0; background.red = png_get_uint_16(buf); background.green = png_get_uint_16(buf + 2); @@ -2359,7 +2377,7 @@ void /* PRIVATE */ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_bytep buffer; - png_size_t i; + size_t i; int state; png_debug(1, "in png_handle_sCAL"); @@ -2429,7 +2447,7 @@ png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) else { - png_size_t heighti = i; + size_t heighti = i; state = 0; if (png_check_fp_number((png_const_charp)buffer, length, @@ -2867,7 +2885,7 @@ png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) { PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); /* The following is safe because of the PNG_SIZE_MAX init above */ - png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/; + png_ptr->unknown_chunk.size = (size_t)length/*SAFE*/; /* 'mode' is a flag array, only the bottom four bits matter here */ png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/; @@ -3149,10 +3167,13 @@ png_check_chunk_length(png_const_structrp png_ptr, const png_uint_32 length) { png_alloc_size_t idat_limit = PNG_UINT_31_MAX; size_t row_factor = - (png_ptr->width * png_ptr->channels * (png_ptr->bit_depth > 8? 2: 1) - + 1 + (png_ptr->interlaced? 6: 0)); + (size_t)png_ptr->width + * (size_t)png_ptr->channels + * (png_ptr->bit_depth > 8? 2: 1) + + 1 + + (png_ptr->interlaced? 6: 0); if (png_ptr->height > PNG_UINT_32_MAX/row_factor) - idat_limit=PNG_UINT_31_MAX; + idat_limit = PNG_UINT_31_MAX; else idat_limit = png_ptr->height * row_factor; row_factor = row_factor > 32566? 32566 : row_factor; @@ -3679,8 +3700,8 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, { case 1: { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + png_bytep sp = row + (size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (size_t)((final_width - 1) >> 3); unsigned int sshift, dshift; unsigned int s_start, s_end; int s_inc; @@ -3806,8 +3827,8 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, case 4: { - png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); - png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + png_bytep sp = row + (size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (size_t)((final_width - 1) >> 1); unsigned int sshift, dshift; unsigned int s_start, s_end; int s_inc; @@ -3869,12 +3890,12 @@ png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, default: { - png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + size_t pixel_bytes = (row_info->pixel_depth >> 3); - png_bytep sp = row + (png_size_t)(row_info->width - 1) + png_bytep sp = row + (size_t)(row_info->width - 1) * pixel_bytes; - png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + png_bytep dp = row + (size_t)(final_width - 1) * pixel_bytes; int jstop = (int)png_pass_inc[pass]; png_uint_32 i; @@ -3911,8 +3932,8 @@ static void png_read_filter_row_sub(png_row_infop row_info, png_bytep row, png_const_bytep prev_row) { - png_size_t i; - png_size_t istop = row_info->rowbytes; + size_t i; + size_t istop = row_info->rowbytes; unsigned int bpp = (row_info->pixel_depth + 7) >> 3; png_bytep rp = row + bpp; @@ -3929,8 +3950,8 @@ static void png_read_filter_row_up(png_row_infop row_info, png_bytep row, png_const_bytep prev_row) { - png_size_t i; - png_size_t istop = row_info->rowbytes; + size_t i; + size_t istop = row_info->rowbytes; png_bytep rp = row; png_const_bytep pp = prev_row; @@ -3945,11 +3966,11 @@ static void png_read_filter_row_avg(png_row_infop row_info, png_bytep row, png_const_bytep prev_row) { - png_size_t i; + size_t i; png_bytep rp = row; png_const_bytep pp = prev_row; unsigned int bpp = (row_info->pixel_depth + 7) >> 3; - png_size_t istop = row_info->rowbytes - bpp; + size_t istop = row_info->rowbytes - bpp; for (i = 0; i < bpp; i++) { @@ -4385,7 +4406,7 @@ png_read_start_row(png_structrp png_ptr) static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; unsigned int max_pixel_depth; - png_size_t row_bytes; + size_t row_bytes; png_debug(1, "in png_read_start_row"); diff --git a/thirdparty/libpng/pngset.c b/thirdparty/libpng/pngset.c index 6f3a1ee11e..7cf54d9248 100644 --- a/thirdparty/libpng/pngset.c +++ b/thirdparty/libpng/pngset.c @@ -1,8 +1,8 @@ /* pngset.c - storage of image information into info struct * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -313,7 +313,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, png_const_charp units, png_charpp params) { - png_size_t length; + size_t length; int i; png_debug1(1, "in %s storage function", "pCAL"); @@ -390,7 +390,7 @@ png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, memcpy(info_ptr->pcal_units, units, length); info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, - (png_size_t)(((unsigned int)nparams + 1) * (sizeof (png_charp))))); + (size_t)(((unsigned int)nparams + 1) * (sizeof (png_charp))))); if (info_ptr->pcal_params == NULL) { @@ -430,7 +430,7 @@ void PNGAPI png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, int unit, png_const_charp swidth, png_const_charp sheight) { - png_size_t lengthw = 0, lengthh = 0; + size_t lengthw = 0, lengthh = 0; png_debug1(1, "in %s storage function", "sCAL"); @@ -691,7 +691,7 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, { png_charp new_iccp_name; png_bytep new_iccp_profile; - png_size_t length; + size_t length; png_debug1(1, "in %s storage function", "iCCP"); @@ -1018,7 +1018,7 @@ png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ info_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); - memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); + memcpy(info_ptr->trans_alpha, trans_alpha, (size_t)num_trans); } png_ptr->trans_alpha = info_ptr->trans_alpha; } @@ -1098,7 +1098,7 @@ png_set_sPLT(png_const_structrp png_ptr, do { - png_size_t length; + size_t length; /* Skip invalid input entries */ if (entries->name == NULL || entries->entries == NULL) @@ -1563,7 +1563,7 @@ png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, #endif void PNGAPI -png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) +png_set_compression_buffer_size(png_structrp png_ptr, size_t size) { if (png_ptr == NULL) return; diff --git a/thirdparty/libpng/pngstruct.h b/thirdparty/libpng/pngstruct.h index d83f971253..699e8ac68a 100644 --- a/thirdparty/libpng/pngstruct.h +++ b/thirdparty/libpng/pngstruct.h @@ -1,8 +1,8 @@ /* pngstruct.h - header file for PNG reference library * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -47,7 +47,7 @@ /* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib * can handle at once. This type need be no larger than 16 bits (so maximum of * 65535), this define allows us to discover how big it is, but limited by the - * maximuum for png_size_t. The value can be overriden in a library build + * maximum for size_t. The value can be overridden in a library build * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably * lower value (e.g. 255 works). A lower value may help memory usage (slightly) * and may even improve performance on some systems (and degrade it on others.) @@ -214,7 +214,7 @@ struct png_struct_def png_uint_32 height; /* height of image in pixels */ png_uint_32 num_rows; /* number of rows in current pass */ png_uint_32 usr_width; /* width of row at start of write */ - png_size_t rowbytes; /* size of row in bytes */ + size_t rowbytes; /* size of row in bytes */ png_uint_32 iwidth; /* width of current interlaced row in pixels */ png_uint_32 row_number; /* current row in interlace pass */ png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ @@ -232,7 +232,7 @@ struct png_struct_def png_bytep try_row; /* buffer to save trial row when filtering */ png_bytep tst_row; /* buffer to save best trial row when filtering */ #endif - png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ + size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ png_uint_32 idat_size; /* current IDAT size for read */ png_uint_32 crc; /* current chunk CRC value */ @@ -307,7 +307,7 @@ struct png_struct_def #endif #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ + png_color_8 shift; /* shift for significant bit transformation */ #endif #if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ @@ -328,10 +328,10 @@ struct png_struct_def png_bytep current_buffer; /* buffer for recently used data */ png_uint_32 push_length; /* size of current input chunk */ png_uint_32 skip_length; /* bytes to skip in input data */ - png_size_t save_buffer_size; /* amount of data now in save_buffer */ - png_size_t save_buffer_max; /* total size of save_buffer */ - png_size_t buffer_size; /* total amount of available input data */ - png_size_t current_buffer_size; /* amount of data now in current_buffer */ + size_t save_buffer_size; /* amount of data now in save_buffer */ + size_t save_buffer_max; /* total size of save_buffer */ + size_t buffer_size; /* total amount of available input data */ + size_t current_buffer_size; /* amount of data now in current_buffer */ int process_mode; /* what push library is currently doing */ int cur_palette; /* current push library palette index */ @@ -451,7 +451,7 @@ struct png_struct_def #endif /* New member added in libpng-1.2.26 */ - png_size_t old_big_row_buf_size; + size_t old_big_row_buf_size; #ifdef PNG_READ_SUPPORTED /* New member added in libpng-1.2.30 */ diff --git a/thirdparty/libpng/pngtrans.c b/thirdparty/libpng/pngtrans.c index 6882f0fd7b..de84aa6d6b 100644 --- a/thirdparty/libpng/pngtrans.c +++ b/thirdparty/libpng/pngtrans.c @@ -1,8 +1,8 @@ /* pngtrans.c - transforms the data in a row (used by both readers and writers) * - * Last changed in libpng 1.6.33 [September 28, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -269,8 +269,8 @@ png_do_invert(png_row_infop row_info, png_bytep row) if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; + size_t i; + size_t istop = row_info->rowbytes; for (i = 0; i < istop; i++) { @@ -283,8 +283,8 @@ png_do_invert(png_row_infop row_info, png_bytep row) row_info->bit_depth == 8) { png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; + size_t i; + size_t istop = row_info->rowbytes; for (i = 0; i < istop; i += 2) { @@ -298,8 +298,8 @@ png_do_invert(png_row_infop row_info, png_bytep row) row_info->bit_depth == 16) { png_bytep rp = row; - png_size_t i; - png_size_t istop = row_info->rowbytes; + size_t i; + size_t istop = row_info->rowbytes; for (i = 0; i < istop; i += 4) { @@ -609,7 +609,7 @@ png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) return; /* The filler channel has gone already */ /* Fix the rowbytes value. */ - row_info->rowbytes = (png_size_t)(dp-row); + row_info->rowbytes = (size_t)(dp-row); } #endif diff --git a/thirdparty/libpng/pngwio.c b/thirdparty/libpng/pngwio.c index 37c7c3a7f0..e5391687a2 100644 --- a/thirdparty/libpng/pngwio.c +++ b/thirdparty/libpng/pngwio.c @@ -1,8 +1,8 @@ /* pngwio.c - functions for data output * - * Last changed in libpng 1.6.24 [August 4, 2016] - * Copyright (c) 1998-2002,2004,2006-2014,2016 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2014,2016,2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -30,7 +30,7 @@ */ void /* PRIVATE */ -png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) +png_write_data(png_structrp png_ptr, png_const_bytep data, size_t length) { /* NOTE: write_data_fn must not change the buffer! */ if (png_ptr->write_data_fn != NULL ) @@ -48,9 +48,9 @@ png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) * than changing the library. */ void PNGCBAPI -png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +png_default_write_data(png_structp png_ptr, png_bytep data, size_t length) { - png_size_t check; + size_t check; if (png_ptr == NULL) return; diff --git a/thirdparty/libpng/pngwrite.c b/thirdparty/libpng/pngwrite.c index a16d77ce00..5bd87f373e 100644 --- a/thirdparty/libpng/pngwrite.c +++ b/thirdparty/libpng/pngwrite.c @@ -1,8 +1,8 @@ /* pngwrite.c - general routines to write a PNG file * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -1636,7 +1636,7 @@ png_write_image_16bit(png_voidp argument) * calculation can be done to 15 bits of accuracy; however, the output needs to * be scaled in the range 0..255*65535, so include that scaling here. */ -# define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) +# define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+((alpha)>>1))/(alpha)) static png_byte png_unpremultiply(png_uint_32 component, png_uint_32 alpha, @@ -2162,8 +2162,7 @@ png_image_write_main(png_voidp argument) static void (PNGCBAPI -image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, - png_size_t size) +image_memory_write)(png_structp png_ptr, png_bytep/*const*/ data, size_t size) { png_image_write_control *display = png_voidcast(png_image_write_control*, png_ptr->io_ptr/*backdoor: png_get_io_ptr(png_ptr)*/); diff --git a/thirdparty/libpng/pngwtran.c b/thirdparty/libpng/pngwtran.c index 377b43e5ca..3a1e0a21d2 100644 --- a/thirdparty/libpng/pngwtran.c +++ b/thirdparty/libpng/pngwtran.c @@ -1,8 +1,8 @@ /* pngwtran.c - transforms the data in a row for PNG writers * - * Last changed in libpng 1.6.26 [October 20, 2016] - * Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -212,9 +212,9 @@ png_do_shift(png_row_infop row_info, png_bytep row, if (row_info->bit_depth < 8) { png_bytep bp = row; - png_size_t i; + size_t i; unsigned int mask; - png_size_t row_bytes = row_info->rowbytes; + size_t row_bytes = row_info->rowbytes; if (bit_depth->gray == 1 && row_info->bit_depth == 2) mask = 0x55; @@ -514,7 +514,7 @@ png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) (png_ptr, /* png_ptr */ row_info, /* row_info: */ /* png_uint_32 width; width of row */ - /* png_size_t rowbytes; number of bytes in row */ + /* size_t rowbytes; number of bytes in row */ /* png_byte color_type; color type of pixels */ /* png_byte bit_depth; bit depth of samples */ /* png_byte channels; number of channels (1-4) */ diff --git a/thirdparty/libpng/pngwutil.c b/thirdparty/libpng/pngwutil.c index 0d4fb1336c..ab431e712c 100644 --- a/thirdparty/libpng/pngwutil.c +++ b/thirdparty/libpng/pngwutil.c @@ -1,8 +1,8 @@ /* pngwutil.c - utilities to write a PNG file * - * Last changed in libpng 1.6.32 [August 24, 2017] - * Copyright (c) 1998-2002,2004,2006-2017 Glenn Randers-Pehrson + * Last changed in libpng 1.6.35 [July 15, 2018] + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * @@ -59,7 +59,7 @@ png_write_sig(png_structrp png_ptr) /* Write the rest of the 8 byte signature */ png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], - (png_size_t)(8 - png_ptr->sig_bytes)); + (size_t)(8 - png_ptr->sig_bytes)); if (png_ptr->sig_bytes < 3) png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; @@ -124,8 +124,7 @@ png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string, * given to png_write_chunk_header(). */ void PNGAPI -png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, - png_size_t length) +png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, size_t length) { /* Write the data, and run the CRC over it */ if (png_ptr == NULL) @@ -160,7 +159,7 @@ png_write_chunk_end(png_structrp png_ptr) /* Write the crc in a single operation */ png_save_uint_32(buf, png_ptr->crc); - png_write_data(png_ptr, buf, (png_size_t)4); + png_write_data(png_ptr, buf, 4); } /* Write a PNG chunk all at once. The type is an array of ASCII characters @@ -174,7 +173,7 @@ png_write_chunk_end(png_structrp png_ptr) */ static void png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, - png_const_bytep data, png_size_t length) + png_const_bytep data, size_t length) { if (png_ptr == NULL) return; @@ -191,7 +190,7 @@ png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, /* This is the API that calls the internal function above. */ void PNGAPI png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, - png_const_bytep data, png_size_t length) + png_const_bytep data, size_t length) { png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data, length); @@ -820,7 +819,7 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, buf[12] = (png_byte)interlace_type; /* Write the chunk */ - png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); + png_write_complete_chunk(png_ptr, png_IHDR, buf, 13); if ((png_ptr->do_filter) == PNG_NO_FILTERS) { @@ -889,7 +888,7 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, buf[0] = pal_ptr->red; buf[1] = pal_ptr->green; buf[2] = pal_ptr->blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); + png_write_chunk_data(png_ptr, buf, 3); } #else @@ -903,7 +902,7 @@ png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, buf[0] = pal_ptr[i].red; buf[1] = pal_ptr[i].green; buf[2] = pal_ptr[i].blue; - png_write_chunk_data(png_ptr, buf, (png_size_t)3); + png_write_chunk_data(png_ptr, buf, 3); } #endif @@ -1075,7 +1074,7 @@ png_write_IEND(png_structrp png_ptr) { png_debug(1, "in png_write_IEND"); - png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0); + png_write_complete_chunk(png_ptr, png_IEND, NULL, 0); png_ptr->mode |= PNG_HAVE_IEND; } @@ -1090,7 +1089,7 @@ png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) /* file_gamma is saved in 1/100,000ths */ png_save_uint_32(buf, (png_uint_32)file_gamma); - png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); + png_write_complete_chunk(png_ptr, png_gAMA, buf, 4); } #endif @@ -1108,7 +1107,7 @@ png_write_sRGB(png_structrp png_ptr, int srgb_intent) "Invalid sRGB rendering intent specified"); buf[0]=(png_byte)srgb_intent; - png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); + png_write_complete_chunk(png_ptr, png_sRGB, buf, 1); } #endif @@ -1182,8 +1181,8 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_uint_32 name_len; png_byte new_name[80]; png_byte entrybuf[10]; - png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); - png_size_t palette_size = entry_size * (png_size_t)spalette->nentries; + size_t entry_size = (spalette->depth == 8 ? 6 : 10); + size_t palette_size = entry_size * (size_t)spalette->nentries; png_sPLT_entryp ep; #ifndef PNG_POINTER_INDEXING_SUPPORTED int i; @@ -1200,10 +1199,9 @@ png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) png_write_chunk_header(png_ptr, png_sPLT, (png_uint_32)(name_len + 2 + palette_size)); - png_write_chunk_data(png_ptr, (png_bytep)new_name, - (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, (size_t)(name_len + 1)); - png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); + png_write_chunk_data(png_ptr, &spalette->depth, 1); /* Loop through each palette entry, writing appropriately */ #ifdef PNG_POINTER_INDEXING_SUPPORTED @@ -1265,7 +1263,7 @@ void /* PRIVATE */ png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) { png_byte buf[4]; - png_size_t size; + size_t size; png_debug(1, "in png_write_sBIT"); @@ -1365,7 +1363,7 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, /* Write the chunk out as it is */ png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, - (png_size_t)num_trans); + (size_t)num_trans); } else if (color_type == PNG_COLOR_TYPE_GRAY) @@ -1380,7 +1378,7 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, } png_save_uint_16(buf, tran->gray); - png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + png_write_complete_chunk(png_ptr, png_tRNS, buf, 2); } else if (color_type == PNG_COLOR_TYPE_RGB) @@ -1400,7 +1398,7 @@ png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, return; } - png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); + png_write_complete_chunk(png_ptr, png_tRNS, buf, 6); } else @@ -1433,7 +1431,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) } buf[0] = back->index; - png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + png_write_complete_chunk(png_ptr, png_bKGD, buf, 1); } else if ((color_type & PNG_COLOR_MASK_COLOR) != 0) @@ -1454,7 +1452,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) return; } - png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + png_write_complete_chunk(png_ptr, png_bKGD, buf, 6); } else @@ -1468,7 +1466,7 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) } png_save_uint_16(buf, back->gray); - png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + png_write_complete_chunk(png_ptr, png_bKGD, buf, 2); } } #endif @@ -1488,7 +1486,7 @@ png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif) for (i = 0; i < num_exif; i++) { buf[0] = exif[i]; - png_write_chunk_data(png_ptr, buf, (png_size_t)1); + png_write_chunk_data(png_ptr, buf, 1); } png_write_chunk_end(png_ptr); @@ -1519,7 +1517,7 @@ png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) for (i = 0; i < num_hist; i++) { png_save_uint_16(buf, hist[i]); - png_write_chunk_data(png_ptr, buf, (png_size_t)2); + png_write_chunk_data(png_ptr, buf, 2); } png_write_chunk_end(png_ptr); @@ -1530,7 +1528,7 @@ png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) /* Write a tEXt chunk */ void /* PRIVATE */ png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, - png_size_t text_len) + size_t text_len) { png_uint_32 key_len; png_byte new_key[80]; @@ -1627,7 +1625,7 @@ png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, png_const_charp lang, png_const_charp lang_key, png_const_charp text) { png_uint_32 key_len, prefix_len; - png_size_t lang_len, lang_key_len; + size_t lang_len, lang_key_len; png_byte new_key[82]; compression_state comp; @@ -1737,7 +1735,7 @@ png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, png_save_int_32(buf + 4, y_offset); buf[8] = (png_byte)unit_type; - png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + png_write_complete_chunk(png_ptr, png_oFFs, buf, 9); } #endif #ifdef PNG_WRITE_pCAL_SUPPORTED @@ -1748,7 +1746,7 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_charpp params) { png_uint_32 purpose_len; - png_size_t units_len, total_len; + size_t units_len, total_len; png_size_tp params_len; png_byte buf[10]; png_byte new_purpose[80]; @@ -1772,7 +1770,7 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, total_len = purpose_len + units_len + 10; params_len = (png_size_tp)png_malloc(png_ptr, - (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (png_size_t)))); + (png_alloc_size_t)((png_alloc_size_t)nparams * (sizeof (size_t)))); /* Find the length of each parameter, making sure we don't count the * null terminator for the last parameter. @@ -1792,8 +1790,8 @@ png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, png_save_int_32(buf + 4, X1); buf[8] = (png_byte)type; buf[9] = (png_byte)nparams; - png_write_chunk_data(png_ptr, buf, (png_size_t)10); - png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); + png_write_chunk_data(png_ptr, buf, 10); + png_write_chunk_data(png_ptr, (png_const_bytep)units, (size_t)units_len); for (i = 0; i < nparams; i++) { @@ -1812,7 +1810,7 @@ png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width, png_const_charp height) { png_byte buf[64]; - png_size_t wlen, hlen, total_len; + size_t wlen, hlen, total_len; png_debug(1, "in png_write_sCAL_s"); @@ -1853,7 +1851,7 @@ png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit, png_save_uint_32(buf + 4, y_pixels_per_unit); buf[8] = (png_byte)unit_type; - png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); + png_write_complete_chunk(png_ptr, png_pHYs, buf, 9); } #endif @@ -1883,7 +1881,7 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) buf[5] = mod_time->minute; buf[6] = mod_time->second; - png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7); + png_write_complete_chunk(png_ptr, png_tIME, buf, 7); } #endif @@ -2073,8 +2071,8 @@ png_write_finish_row(png_structrp png_ptr) { if (png_ptr->prev_row != NULL) memset(png_ptr->prev_row, 0, - (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* - png_ptr->usr_bit_depth, png_ptr->width)) + 1); + PNG_ROWBYTES(png_ptr->usr_channels * + png_ptr->usr_bit_depth, png_ptr->width) + 1); return; } @@ -2130,7 +2128,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { - sp = row + (png_size_t)(i >> 3); + sp = row + (size_t)(i >> 3); value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; d |= (value << shift); @@ -2168,7 +2166,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { - sp = row + (png_size_t)(i >> 2); + sp = row + (size_t)(i >> 2); value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; d |= (value << shift); @@ -2204,7 +2202,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { - sp = row + (png_size_t)(i >> 1); + sp = row + (size_t)(i >> 1); value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; d |= (value << shift); @@ -2230,7 +2228,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) png_bytep dp; png_uint_32 i; png_uint_32 row_width = row_info->width; - png_size_t pixel_bytes; + size_t pixel_bytes; /* Start at the beginning */ dp = row; @@ -2243,7 +2241,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) i += png_pass_inc[pass]) { /* Find out where the original pixel is */ - sp = row + (png_size_t)i * pixel_bytes; + sp = row + (size_t)i * pixel_bytes; /* Move the pixel */ if (dp != sp) @@ -2274,16 +2272,16 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) */ static void /* PRIVATE */ png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t row_bytes); + size_t row_bytes); #ifdef PNG_WRITE_FILTER_SUPPORTED -static png_size_t /* PRIVATE */ +static size_t /* PRIVATE */ png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) + size_t row_bytes, size_t lmins) { png_bytep rp, dp, lp; - png_size_t i; - png_size_t sum = 0; + size_t i; + size_t sum = 0; unsigned int v; png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; @@ -2318,10 +2316,10 @@ png_setup_sub_row(png_structrp png_ptr, const png_uint_32 bpp, static void /* PRIVATE */ png_setup_sub_row_only(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes) + size_t row_bytes) { png_bytep rp, dp, lp; - png_size_t i; + size_t i; png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; @@ -2338,13 +2336,12 @@ png_setup_sub_row_only(png_structrp png_ptr, const png_uint_32 bpp, } } -static png_size_t /* PRIVATE */ -png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes, - const png_size_t lmins) +static size_t /* PRIVATE */ +png_setup_up_row(png_structrp png_ptr, size_t row_bytes, size_t lmins) { png_bytep rp, dp, pp; - png_size_t i; - png_size_t sum = 0; + size_t i; + size_t sum = 0; unsigned int v; png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; @@ -2367,10 +2364,10 @@ png_setup_up_row(png_structrp png_ptr, const png_size_t row_bytes, return (sum); } static void /* PRIVATE */ -png_setup_up_row_only(png_structrp png_ptr, const png_size_t row_bytes) +png_setup_up_row_only(png_structrp png_ptr, size_t row_bytes) { png_bytep rp, dp, pp; - png_size_t i; + size_t i; png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; @@ -2382,13 +2379,13 @@ png_setup_up_row_only(png_structrp png_ptr, const png_size_t row_bytes) } } -static png_size_t /* PRIVATE */ +static size_t /* PRIVATE */ png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) + size_t row_bytes, size_t lmins) { png_bytep rp, dp, pp, lp; png_uint_32 i; - png_size_t sum = 0; + size_t sum = 0; unsigned int v; png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; @@ -2424,7 +2421,7 @@ png_setup_avg_row(png_structrp png_ptr, const png_uint_32 bpp, } static void /* PRIVATE */ png_setup_avg_row_only(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes) + size_t row_bytes) { png_bytep rp, dp, pp, lp; png_uint_32 i; @@ -2444,13 +2441,13 @@ png_setup_avg_row_only(png_structrp png_ptr, const png_uint_32 bpp, } } -static png_size_t /* PRIVATE */ +static size_t /* PRIVATE */ png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes, const png_size_t lmins) + size_t row_bytes, size_t lmins) { png_bytep rp, dp, pp, cp, lp; - png_size_t i; - png_size_t sum = 0; + size_t i; + size_t sum = 0; unsigned int v; png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; @@ -2507,10 +2504,10 @@ png_setup_paeth_row(png_structrp png_ptr, const png_uint_32 bpp, } static void /* PRIVATE */ png_setup_paeth_row_only(png_structrp png_ptr, const png_uint_32 bpp, - const png_size_t row_bytes) + size_t row_bytes) { png_bytep rp, dp, pp, cp, lp; - png_size_t i; + size_t i; png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; @@ -2559,8 +2556,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) png_bytep row_buf; png_bytep best_row; png_uint_32 bpp; - png_size_t mins; - png_size_t row_bytes = row_info->rowbytes; + size_t mins; + size_t row_bytes = row_info->rowbytes; png_debug(1, "in png_write_find_filter"); @@ -2615,8 +2612,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) * 'none' filter. */ png_bytep rp; - png_size_t sum = 0; - png_size_t i; + size_t sum = 0; + size_t i; unsigned int v; { @@ -2644,8 +2641,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) else if ((filter_to_do & PNG_FILTER_SUB) != 0) { - png_size_t sum; - png_size_t lmins = mins; + size_t sum; + size_t lmins = mins; sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins); @@ -2670,8 +2667,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) else if ((filter_to_do & PNG_FILTER_UP) != 0) { - png_size_t sum; - png_size_t lmins = mins; + size_t sum; + size_t lmins = mins; sum = png_setup_up_row(png_ptr, row_bytes, lmins); @@ -2696,8 +2693,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) else if ((filter_to_do & PNG_FILTER_AVG) != 0) { - png_size_t sum; - png_size_t lmins = mins; + size_t sum; + size_t lmins = mins; sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins); @@ -2722,8 +2719,8 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) else if ((filter_to_do & PNG_FILTER_PAETH) != 0) { - png_size_t sum; - png_size_t lmins = mins; + size_t sum; + size_t lmins = mins; sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins); @@ -2748,7 +2745,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) /* Do the actual writing of a previously filtered row. */ static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, - png_size_t full_row_length/*includes filter byte*/) + size_t full_row_length/*includes filter byte*/) { png_debug(1, "in png_write_filtered_row"); diff --git a/thirdparty/mbedtls/1453.diff b/thirdparty/mbedtls/1453.diff index fef60fc0e6..6630ad861f 100644 --- a/thirdparty/mbedtls/1453.diff +++ b/thirdparty/mbedtls/1453.diff @@ -1,5 +1,5 @@ diff --git a/library/entropy_poll.c b/library/entropy_poll.c -index 67900c46..cefe882d 100644 +index 67900c46c8..cefe882d2a 100644 --- a/library/entropy_poll.c +++ b/library/entropy_poll.c @@ -54,28 +54,43 @@ @@ -54,7 +54,7 @@ index 67900c46..cefe882d 100644 return( 0 ); diff --git a/library/x509_crt.c b/library/x509_crt.c -index 290c1eb3..038eae02 100644 +index 290c1eb3d1..3cf1743821 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -65,6 +65,19 @@ @@ -81,7 +81,7 @@ index 290c1eb3..038eae02 100644 char filename[MAX_PATH]; char *p; size_t len = strlen( path ); -+ int lengthAsInt = 0; ++ int length_as_int = 0; WIN32_FIND_DATAW file_data; HANDLE hFind; @@ -90,7 +90,7 @@ index 290c1eb3..038eae02 100644 filename[len++] = '*'; - w_ret = MultiByteToWideChar( CP_ACP, 0, filename, (int)len, szDir, -+ if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) ) ++ if ( FAILED ( SizeTToInt( len, &length_as_int ) ) ) + return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); + + /* @@ -101,7 +101,7 @@ index 290c1eb3..038eae02 100644 + * incoming string are less than MAX_PATH to avoid a buffer overrun with + * MultiByteToWideChar(). + */ -+ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir, ++ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, length_as_int, szDir, MAX_PATH - 3 ); if( w_ret == 0 ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); @@ -109,12 +109,12 @@ index 290c1eb3..038eae02 100644 if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) continue; -+ if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) ) ++ if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &length_as_int ) ) ) + return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); + w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName, - lstrlenW( file_data.cFileName ), -+ lengthAsInt, ++ length_as_int, p, (int) len - 1, NULL, NULL ); if( w_ret == 0 ) diff --git a/thirdparty/mbedtls/include/mbedtls/aes.h b/thirdparty/mbedtls/include/mbedtls/aes.h index dd5c1183a5..f6603d5962 100644 --- a/thirdparty/mbedtls/include/mbedtls/aes.h +++ b/thirdparty/mbedtls/include/mbedtls/aes.h @@ -13,6 +13,11 @@ * <em>ISO/IEC 18033-2:2006: Information technology -- Security * techniques -- Encryption algorithms -- Part 2: Asymmetric * ciphers</em>. + * + * The AES-XTS block mode is standardized by NIST SP 800-38E + * <https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38e.pdf> + * and described in detail by IEEE P1619 + * <https://ieeexplore.ieee.org/servlet/opac?punumber=4375278>. */ /* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved. @@ -89,6 +94,19 @@ typedef struct } mbedtls_aes_context; +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief The AES XTS context-type definition. + */ +typedef struct +{ + mbedtls_aes_context crypt; /*!< The AES context to use for AES block + encryption or decryption. */ + mbedtls_aes_context tweak; /*!< The AES context used for tweak + computation. */ +} mbedtls_aes_xts_context; +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #else /* MBEDTLS_AES_ALT */ #include "aes_alt.h" #endif /* MBEDTLS_AES_ALT */ @@ -110,6 +128,25 @@ void mbedtls_aes_init( mbedtls_aes_context *ctx ); */ void mbedtls_aes_free( mbedtls_aes_context *ctx ); +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function initializes the specified AES XTS context. + * + * It must be the first API called before using + * the context. + * + * \param ctx The AES XTS context to initialize. + */ +void mbedtls_aes_xts_init( mbedtls_aes_xts_context *ctx ); + +/** + * \brief This function releases and clears the specified AES XTS context. + * + * \param ctx The AES XTS context to clear. + */ +void mbedtls_aes_xts_free( mbedtls_aes_xts_context *ctx ); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + /** * \brief This function sets the encryption key. * @@ -142,6 +179,44 @@ int mbedtls_aes_setkey_enc( mbedtls_aes_context *ctx, const unsigned char *key, int mbedtls_aes_setkey_dec( mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits ); +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function prepares an XTS context for encryption and + * sets the encryption key. + * + * \param ctx The AES XTS context to which the key should be bound. + * \param key The encryption key. This is comprised of the XTS key1 + * concatenated with the XTS key2. + * \param keybits The size of \p key passed in bits. Valid options are: + * <ul><li>256 bits (each of key1 and key2 is a 128-bit key)</li> + * <li>512 bits (each of key1 and key2 is a 256-bit key)</li></ul> + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +int mbedtls_aes_xts_setkey_enc( mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits ); + +/** + * \brief This function prepares an XTS context for decryption and + * sets the decryption key. + * + * \param ctx The AES XTS context to which the key should be bound. + * \param key The decryption key. This is comprised of the XTS key1 + * concatenated with the XTS key2. + * \param keybits The size of \p key passed in bits. Valid options are: + * <ul><li>256 bits (each of key1 and key2 is a 128-bit key)</li> + * <li>512 bits (each of key1 and key2 is a 256-bit key)</li></ul> + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +int mbedtls_aes_xts_setkey_dec( mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits ); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + /** * \brief This function performs an AES single-block encryption or * decryption operation. @@ -213,6 +288,49 @@ int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx, unsigned char *output ); #endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function performs an AES-XTS encryption or decryption + * operation for an entire XTS data unit. + * + * AES-XTS encrypts or decrypts blocks based on their location as + * defined by a data unit number. The data unit number must be + * provided by \p data_unit. + * + * NIST SP 800-38E limits the maximum size of a data unit to 2^20 + * AES blocks. If the data unit is larger than this, this function + * returns #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH. + * + * \param ctx The AES XTS context to use for AES XTS operations. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT. + * \param length The length of a data unit in bytes. This can be any + * length between 16 bytes and 2^24 bytes inclusive + * (between 1 and 2^20 block cipher blocks). + * \param data_unit The address of the data unit encoded as an array of 16 + * bytes in little-endian format. For disk encryption, this + * is typically the index of the block device sector that + * contains the data. + * \param input The buffer holding the input data (which is an entire + * data unit). This function reads \p length bytes from \p + * input. + * \param output The buffer holding the output data (which is an entire + * data unit). This function writes \p length bytes to \p + * output. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH if \p length is + * smaller than an AES block in size (16 bytes) or if \p + * length is larger than 2^20 blocks (16 MiB). + */ +int mbedtls_aes_crypt_xts( mbedtls_aes_xts_context *ctx, + int mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output ); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #if defined(MBEDTLS_CIPHER_MODE_CFB) /** * \brief This function performs an AES-CFB128 encryption or decryption @@ -296,6 +414,56 @@ int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx, unsigned char *output ); #endif /*MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/** + * \brief This function performs an AES-OFB (Output Feedback Mode) + * encryption or decryption operation. + * + * For OFB, you must set up the context with + * mbedtls_aes_setkey_enc(), regardless of whether you are + * performing an encryption or decryption operation. This is + * because OFB mode uses the same key schedule for encryption and + * decryption. + * + * The OFB operation is identical for encryption or decryption, + * therefore no operation mode needs to be specified. + * + * \note Upon exit, the content of iv, the Initialisation Vector, is + * updated so that you can call the same function again on the next + * block(s) of data and get the same result as if it was encrypted + * in one call. This allows a "streaming" usage, by initialising + * iv_off to 0 before the first call, and preserving its value + * between calls. + * + * For non-streaming use, the iv should be initialised on each call + * to a unique value, and iv_off set to 0 on each call. + * + * If you need to retain the contents of the initialisation vector, + * you must either save it manually or use the cipher module + * instead. + * + * \warning For the OFB mode, the initialisation vector must be unique + * every encryption operation. Reuse of an initialisation vector + * will compromise security. + * + * \param ctx The AES context to use for encryption or decryption. + * \param length The length of the input data. + * \param iv_off The offset in IV (updated after use). + * \param iv The initialization vector (updated after use). + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * + * \return \c 0 on success. + */ +int mbedtls_aes_crypt_ofb( mbedtls_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) /** * \brief This function performs an AES-CTR encryption or decryption diff --git a/thirdparty/mbedtls/include/mbedtls/bn_mul.h b/thirdparty/mbedtls/include/mbedtls/bn_mul.h index f4b2b561d1..b587317d95 100644 --- a/thirdparty/mbedtls/include/mbedtls/bn_mul.h +++ b/thirdparty/mbedtls/include/mbedtls/bn_mul.h @@ -49,7 +49,14 @@ /* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */ #if defined(__GNUC__) && \ ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) -#if defined(__i386__) + +/* + * Disable use of the i386 assembly code below if option -O0, to disable all + * compiler optimisations, is passed, detected with __OPTIMIZE__ + * This is done as the number of registers used in the assembly code doesn't + * work with the -O0 option. + */ +#if defined(__i386__) && defined(__OPTIMIZE__) #define MULADDC_INIT \ asm( \ @@ -142,7 +149,7 @@ "movl %%esi, %3 \n\t" \ : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ - : "eax", "ecx", "edx", "esi", "edi" \ + : "eax", "ebx", "ecx", "edx", "esi", "edi" \ ); #else @@ -154,7 +161,7 @@ "movl %%esi, %3 \n\t" \ : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ - : "eax", "ecx", "edx", "esi", "edi" \ + : "eax", "ebx", "ecx", "edx", "esi", "edi" \ ); #endif /* SSE2 */ #endif /* i386 */ diff --git a/thirdparty/mbedtls/include/mbedtls/ccm.h b/thirdparty/mbedtls/include/mbedtls/ccm.h index 8585ce5e7c..5d727e7cca 100644 --- a/thirdparty/mbedtls/include/mbedtls/ccm.h +++ b/thirdparty/mbedtls/include/mbedtls/ccm.h @@ -14,6 +14,18 @@ * <li>Nonce - A unique value that is assigned to the payload and the * associated data.</li></ul> * + * Definition of CCM: + * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + * RFC 3610 "Counter with CBC-MAC (CCM)" + * + * Related: + * RFC 5116 "An Interface and Algorithms for Authenticated Encryption" + * + * Definition of CCM*: + * IEEE 802.15.4 - IEEE Standard for Local and metropolitan area networks + * Integer representation is fixed most-significant-octet-first order and + * the representation of octets is most-significant-bit-first order. This is + * consistent with RFC 3610. */ /* * Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved @@ -102,7 +114,6 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx ); /** * \brief This function encrypts a buffer using CCM. * - * * \note The tag is written to a separate buffer. To concatenate * the \p tag with the \p output, as done in <em>RFC-3610: * Counter with CBC-MAC (CCM)</em>, use @@ -112,15 +123,17 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx ); * \param ctx The CCM context to use for encryption. * \param length The length of the input data in Bytes. * \param iv Initialization vector (nonce). - * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. * \param add The additional data field. * \param add_len The length of additional data in Bytes. * Must be less than 2^16 - 2^8. * \param input The buffer holding the input data. * \param output The buffer holding the output data. * Must be at least \p length Bytes wide. - * \param tag The buffer holding the tag. - * \param tag_len The length of the tag to generate in Bytes: + * \param tag The buffer holding the authentication field. + * \param tag_len The length of the authentication field to generate in Bytes: * 4, 6, 8, 10, 12, 14 or 16. * * \return \c 0 on success. @@ -133,21 +146,64 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, unsigned char *tag, size_t tag_len ); /** + * \brief This function encrypts a buffer using CCM*. + * + * \note The tag is written to a separate buffer. To concatenate + * the \p tag with the \p output, as done in <em>RFC-3610: + * Counter with CBC-MAC (CCM)</em>, use + * \p tag = \p output + \p length, and make sure that the + * output buffer is at least \p length + \p tag_len wide. + * + * \note When using this function in a variable tag length context, + * the tag length has to be encoded into the \p iv passed to + * this function. + * + * \param ctx The CCM context to use for encryption. + * \param length The length of the input data in Bytes. + * \param iv Initialization vector (nonce). + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param tag The buffer holding the authentication field. + * \param tag_len The length of the authentication field to generate in Bytes: + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_star_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ); + +/** * \brief This function performs a CCM authenticated decryption of a * buffer. * * \param ctx The CCM context to use for decryption. * \param length The length of the input data in Bytes. - * \param iv Initialization vector. - * \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13. + * \param iv Initialization vector (nonce). + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. * \param add The additional data field. * \param add_len The length of additional data in Bytes. * Must be less than 2^16 - 2^8. * \param input The buffer holding the input data. * \param output The buffer holding the output data. * Must be at least \p length Bytes wide. - * \param tag The buffer holding the tag. - * \param tag_len The length of the tag in Bytes. + * \param tag The buffer holding the authentication field. + * \param tag_len The length of the authentication field in Bytes. * 4, 6, 8, 10, 12, 14 or 16. * * \return \c 0 on success. This indicates that the message is authentic. @@ -160,6 +216,43 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, const unsigned char *input, unsigned char *output, const unsigned char *tag, size_t tag_len ); +/** + * \brief This function performs a CCM* authenticated decryption of a + * buffer. + * + * \note When using this function in a variable tag length context, + * the tag length has to be decoded from \p iv and passed to + * this function as \p tag_len. (\p tag needs to be adjusted + * accordingly.) + * + * \param ctx The CCM context to use for decryption. + * \param length The length of the input data in Bytes. + * \param iv Initialization vector (nonce). + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param add The additional data field. + * \param add_len The length of additional data in Bytes. + * Must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. + * \param output The buffer holding the output data. + * Must be at least \p length Bytes wide. + * \param tag The buffer holding the authentication field. + * \param tag_len The length of the authentication field in Bytes. + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. + * \return A cipher-specific error code on calculation failure. + */ +int mbedtls_ccm_star_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ); #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) /** diff --git a/thirdparty/mbedtls/include/mbedtls/chacha20.h b/thirdparty/mbedtls/include/mbedtls/chacha20.h new file mode 100644 index 0000000000..47bd7d38b9 --- /dev/null +++ b/thirdparty/mbedtls/include/mbedtls/chacha20.h @@ -0,0 +1,212 @@ +/** + * \file chacha20.h + * + * \brief This file contains ChaCha20 definitions and functions. + * + * ChaCha20 is a stream cipher that can encrypt and decrypt + * information. ChaCha was created by Daniel Bernstein as a variant of + * its Salsa cipher https://cr.yp.to/chacha/chacha-20080128.pdf + * ChaCha20 is the variant with 20 rounds, that was also standardized + * in RFC 7539. + * + * \author Daniel King <damaki.gh@gmail.com> + */ + +/* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CHACHA20_H +#define MBEDTLS_CHACHA20_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include <stdint.h> +#include <stddef.h> + +#define MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA -0x0051 /**< Invalid input parameter(s). */ +#define MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE -0x0053 /**< Feature not available. For example, s part of the API is not implemented. */ +#define MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED -0x0055 /**< Chacha20 hardware accelerator failed. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_CHACHA20_ALT) + +typedef struct +{ + uint32_t state[16]; /*! The state (before round operations). */ + uint8_t keystream8[64]; /*! Leftover keystream bytes. */ + size_t keystream_bytes_used; /*! Number of keystream bytes already used. */ +} +mbedtls_chacha20_context; + +#else /* MBEDTLS_CHACHA20_ALT */ +#include "chacha20_alt.h" +#endif /* MBEDTLS_CHACHA20_ALT */ + +/** + * \brief This function initializes the specified ChaCha20 context. + * + * It must be the first API called before using + * the context. + * + * It is usually followed by calls to + * \c mbedtls_chacha20_setkey() and + * \c mbedtls_chacha20_starts(), then one or more calls to + * to \c mbedtls_chacha20_update(), and finally to + * \c mbedtls_chacha20_free(). + * + * \param ctx The ChaCha20 context to initialize. + */ +void mbedtls_chacha20_init( mbedtls_chacha20_context *ctx ); + +/** + * \brief This function releases and clears the specified ChaCha20 context. + * + * \param ctx The ChaCha20 context to clear. + */ +void mbedtls_chacha20_free( mbedtls_chacha20_context *ctx ); + +/** + * \brief This function sets the encryption/decryption key. + * + * \note After using this function, you must also call + * \c mbedtls_chacha20_starts() to set a nonce before you + * start encrypting/decrypting data with + * \c mbedtls_chacha_update(). + * + * \param ctx The ChaCha20 context to which the key should be bound. + * \param key The encryption/decryption key. Must be 32 bytes in length. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or key is NULL. + */ +int mbedtls_chacha20_setkey( mbedtls_chacha20_context *ctx, + const unsigned char key[32] ); + +/** + * \brief This function sets the nonce and initial counter value. + * + * \note A ChaCha20 context can be re-used with the same key by + * calling this function to change the nonce. + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality guarantees for the + * messages encrypted with the same nonce and key. + * + * \param ctx The ChaCha20 context to which the nonce should be bound. + * \param nonce The nonce. Must be 12 bytes in size. + * \param counter The initial counter value. This is usually 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or nonce is + * NULL. + */ +int mbedtls_chacha20_starts( mbedtls_chacha20_context* ctx, + const unsigned char nonce[12], + uint32_t counter ); + +/** + * \brief This function encrypts or decrypts data. + * + * Since ChaCha20 is a stream cipher, the same operation is + * used for encrypting and decrypting data. + * + * \note The \p input and \p output pointers must either be equal or + * point to non-overlapping buffers. + * + * \note \c mbedtls_chacha20_setkey() and + * \c mbedtls_chacha20_starts() must be called at least once + * to setup the context before this function can be called. + * + * \note This function can be called multiple times in a row in + * order to encrypt of decrypt data piecewise with the same + * key and nonce. + * + * \param ctx The ChaCha20 context to use for encryption or decryption. + * \param size The length of the input data in bytes. + * \param input The buffer holding the input data. + * This pointer can be NULL if size == 0. + * \param output The buffer holding the output data. + * Must be able to hold \p size bytes. + * This pointer can be NULL if size == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if the ctx, input, or + * output pointers are NULL. + */ +int mbedtls_chacha20_update( mbedtls_chacha20_context *ctx, + size_t size, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function encrypts or decrypts data with ChaCha20 and + * the given key and nonce. + * + * Since ChaCha20 is a stream cipher, the same operation is + * used for encrypting and decrypting data. + * + * \warning You must never use the same (key, nonce) pair more than + * once. This would void any confidentiality guarantees for + * the messages encrypted with the same nonce and key. + * + * \note The \p input and \p output pointers must either be equal or + * point to non-overlapping buffers. + * + * \param key The encryption/decryption key. Must be 32 bytes in length. + * \param nonce The nonce. Must be 12 bytes in size. + * \param counter The initial counter value. This is usually 0. + * \param size The length of the input data in bytes. + * \param input The buffer holding the input data. + * This pointer can be NULL if size == 0. + * \param output The buffer holding the output data. + * Must be able to hold \p size bytes. + * This pointer can be NULL if size == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if key, nonce, input, + * or output is NULL. + */ +int mbedtls_chacha20_crypt( const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter, + size_t size, + const unsigned char* input, + unsigned char* output ); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The ChaCha20 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_chacha20_self_test( int verbose ); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CHACHA20_H */ diff --git a/thirdparty/mbedtls/include/mbedtls/chachapoly.h b/thirdparty/mbedtls/include/mbedtls/chachapoly.h new file mode 100644 index 0000000000..42b2b230c5 --- /dev/null +++ b/thirdparty/mbedtls/include/mbedtls/chachapoly.h @@ -0,0 +1,355 @@ +/** + * \file chachapoly.h + * + * \brief This file contains the AEAD-ChaCha20-Poly1305 definitions and + * functions. + * + * ChaCha20-Poly1305 is an algorithm for Authenticated Encryption + * with Associated Data (AEAD) that can be used to encrypt and + * authenticate data. It is based on ChaCha20 and Poly1305 by Daniel + * Bernstein and was standardized in RFC 7539. + * + * \author Daniel King <damaki.gh@gmail.com> + */ + +/* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CHACHAPOLY_H +#define MBEDTLS_CHACHAPOLY_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +/* for shared error codes */ +#include "poly1305.h" + +#define MBEDTLS_ERR_CHACHAPOLY_BAD_STATE -0x0054 /**< The requested operation is not permitted in the current state. */ +#define MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED -0x0056 /**< Authenticated decryption failed: data was not authentic. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + MBEDTLS_CHACHAPOLY_ENCRYPT, /**< The mode value for performing encryption. */ + MBEDTLS_CHACHAPOLY_DECRYPT /**< The mode value for performing decryption. */ +} +mbedtls_chachapoly_mode_t; + +#if !defined(MBEDTLS_CHACHAPOLY_ALT) + +#include "chacha20.h" + +typedef struct +{ + mbedtls_chacha20_context chacha20_ctx; /**< The ChaCha20 context. */ + mbedtls_poly1305_context poly1305_ctx; /**< The Poly1305 context. */ + uint64_t aad_len; /**< The length (bytes) of the Additional Authenticated Data. */ + uint64_t ciphertext_len; /**< The length (bytes) of the ciphertext. */ + int state; /**< The current state of the context. */ + mbedtls_chachapoly_mode_t mode; /**< Cipher mode (encrypt or decrypt). */ +} +mbedtls_chachapoly_context; + +#else /* !MBEDTLS_CHACHAPOLY_ALT */ +#include "chachapoly_alt.h" +#endif /* !MBEDTLS_CHACHAPOLY_ALT */ + +/** + * \brief This function initializes the specified ChaCha20-Poly1305 context. + * + * It must be the first API called before using + * the context. It must be followed by a call to + * \c mbedtls_chachapoly_setkey() before any operation can be + * done, and to \c mbedtls_chachapoly_free() once all + * operations with that context have been finished. + * + * In order to encrypt or decrypt full messages at once, for + * each message you should make a single call to + * \c mbedtls_chachapoly_crypt_and_tag() or + * \c mbedtls_chachapoly_auth_decrypt(). + * + * In order to encrypt messages piecewise, for each + * message you should make a call to + * \c mbedtls_chachapoly_starts(), then 0 or more calls to + * \c mbedtls_chachapoly_update_aad(), then 0 or more calls to + * \c mbedtls_chachapoly_update(), then one call to + * \c mbedtls_chachapoly_finish(). + * + * \warning Decryption with the piecewise API is discouraged! Always + * use \c mbedtls_chachapoly_auth_decrypt() when possible! + * + * If however this is not possible because the data is too + * large to fit in memory, you need to: + * + * - call \c mbedtls_chachapoly_starts() and (if needed) + * \c mbedtls_chachapoly_update_aad() as above, + * - call \c mbedtls_chachapoly_update() multiple times and + * ensure its output (the plaintext) is NOT used in any other + * way than placing it in temporary storage at this point, + * - call \c mbedtls_chachapoly_finish() to compute the + * authentication tag and compared it in constant time to the + * tag received with the ciphertext. + * + * If the tags are not equal, you must immediately discard + * all previous outputs of \c mbedtls_chachapoly_update(), + * otherwise you can now safely use the plaintext. + * + * \param ctx The ChachaPoly context to initialize. + */ +void mbedtls_chachapoly_init( mbedtls_chachapoly_context *ctx ); + +/** + * \brief This function releases and clears the specified ChaCha20-Poly1305 context. + * + * \param ctx The ChachaPoly context to clear. + */ +void mbedtls_chachapoly_free( mbedtls_chachapoly_context *ctx ); + +/** + * \brief This function sets the ChaCha20-Poly1305 symmetric encryption key. + * + * \param ctx The ChaCha20-Poly1305 context to which the key should be + * bound. + * \param key The 256-bit (32 bytes) key. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx or \p key are NULL. + */ +int mbedtls_chachapoly_setkey( mbedtls_chachapoly_context *ctx, + const unsigned char key[32] ); + +/** + * \brief This function starts a ChaCha20-Poly1305 encryption or + * decryption operation. + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality and authenticity + * guarantees for the messages encrypted with the same nonce + * and key. + * + * \note If the context is being used for AAD only (no data to + * encrypt or decrypt) then \p mode can be set to any value. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context. + * \param nonce The nonce/IV to use for the message. Must be 12 bytes. + * \param mode The operation to perform: #MBEDTLS_CHACHAPOLY_ENCRYPT or + * #MBEDTLS_CHACHAPOLY_DECRYPT (discouraged, see warning). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx or \p mac are NULL. + */ +int mbedtls_chachapoly_starts( mbedtls_chachapoly_context *ctx, + const unsigned char nonce[12], + mbedtls_chachapoly_mode_t mode ); + +/** + * \brief This function feeds additional data to be authenticated + * into an ongoing ChaCha20-Poly1305 operation. + * + * The Additional Authenticated Data (AAD), also called + * Associated Data (AD) is only authenticated but not + * encrypted nor included in the encrypted output. It is + * usually transmitted separately from the ciphertext or + * computed locally by each party. + * + * \note This function is called before data is encrypted/decrypted. + * I.e. call this function to process the AAD before calling + * \c mbedtls_chachapoly_update(). + * + * You may call this function multiple times to process + * an arbitrary amount of AAD. It is permitted to call + * this function 0 times, if no AAD is used. + * + * This function cannot be called any more if data has + * been processed by \c mbedtls_chachapoly_update(), + * or if the context has been finished. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context to use. + * \param aad_len The length (in bytes) of the AAD. The length has no + * restrictions. + * \param aad Buffer containing the AAD. + * This pointer can be NULL if aad_len == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx or \p aad are NULL. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operations has not been started or has been + * finished, or if the AAD has been finished. + */ +int mbedtls_chachapoly_update_aad( mbedtls_chachapoly_context *ctx, + const unsigned char *aad, + size_t aad_len ); + +/** + * \brief Thus function feeds data to be encrypted or decrypted + * into an on-going ChaCha20-Poly1305 + * operation. + * + * The direction (encryption or decryption) depends on the + * mode that was given when calling + * \c mbedtls_chachapoly_starts(). + * + * You may call this function multiple times to process + * an arbitrary amount of data. It is permitted to call + * this function 0 times, if no data is to be encrypted + * or decrypted. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context to use. + * \param len The length (in bytes) of the data to encrypt or decrypt. + * \param input The buffer containing the data to encrypt or decrypt. + * This pointer can be NULL if len == 0. + * \param output The buffer to where the encrypted or decrypted data is written. + * Must be able to hold \p len bytes. + * This pointer can be NULL if len == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx, \p input, or \p output are NULL. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operation has not been started or has been + * finished. + */ +int mbedtls_chachapoly_update( mbedtls_chachapoly_context *ctx, + size_t len, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief This function finished the ChaCha20-Poly1305 operation and + * generates the MAC (authentication tag). + * + * \param ctx The ChaCha20-Poly1305 context to use. + * \param mac The buffer to where the 128-bit (16 bytes) MAC is written. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx or \p mac are NULL. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operation has not been started or has been + * finished. + */ +int mbedtls_chachapoly_finish( mbedtls_chachapoly_context *ctx, + unsigned char mac[16] ); + +/** + * \brief This function performs a complete ChaCha20-Poly1305 + * authenticated encryption with the previously-set key. + * + * \note Before using this function, you must set the key with + * \c mbedtls_chachapoly_setkey(). + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality and authenticity + * guarantees for the messages encrypted with the same nonce + * and key. + * + * \param ctx The ChaCha20-Poly1305 context to use (holds the key). + * \param length The length (in bytes) of the data to encrypt or decrypt. + * \param nonce The 96-bit (12 bytes) nonce/IV to use. + * \param aad The buffer containing the additional authenticated data (AAD). + * This pointer can be NULL if aad_len == 0. + * \param aad_len The length (in bytes) of the AAD data to process. + * \param input The buffer containing the data to encrypt or decrypt. + * This pointer can be NULL if ilen == 0. + * \param output The buffer to where the encrypted or decrypted data is written. + * This pointer can be NULL if ilen == 0. + * \param tag The buffer to where the computed 128-bit (16 bytes) MAC is written. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if one or more of the required parameters are NULL. + */ +int mbedtls_chachapoly_encrypt_and_tag( mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16] ); + +/** + * \brief This function performs a complete ChaCha20-Poly1305 + * authenticated decryption with the previously-set key. + * + * \note Before using this function, you must set the key with + * \c mbedtls_chachapoly_setkey(). + * + * \param ctx The ChaCha20-Poly1305 context to use (holds the key). + * \param length The length (in bytes) of the data to decrypt. + * \param nonce The 96-bit (12 bytes) nonce/IV to use. + * \param aad The buffer containing the additional authenticated data (AAD). + * This pointer can be NULL if aad_len == 0. + * \param aad_len The length (in bytes) of the AAD data to process. + * \param tag The buffer holding the authentication tag. + * \param input The buffer containing the data to decrypt. + * This pointer can be NULL if ilen == 0. + * \param output The buffer to where the decrypted data is written. + * This pointer can be NULL if ilen == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if one or more of the required parameters are NULL. + * \return #MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED + * if the data was not authentic. + */ +int mbedtls_chachapoly_auth_decrypt( mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char tag[16], + const unsigned char *input, + unsigned char *output ); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The ChaCha20-Poly1305 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_chachapoly_self_test( int verbose ); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CHACHAPOLY_H */ diff --git a/thirdparty/mbedtls/include/mbedtls/check_config.h b/thirdparty/mbedtls/include/mbedtls/check_config.h index be80332963..9e6bb8a46a 100644 --- a/thirdparty/mbedtls/include/mbedtls/check_config.h +++ b/thirdparty/mbedtls/include/mbedtls/check_config.h @@ -4,7 +4,7 @@ * \brief Consistency checks for configuration options */ /* - * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * Copyright (C) 2006-2018, ARM Limited, All Rights Reserved * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -87,6 +87,11 @@ #error "MBEDTLS_CMAC_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_NIST_KW_C) && \ + ( !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_CIPHER_C) ) +#error "MBEDTLS_NIST_KW_C defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_ECDH_C) && !defined(MBEDTLS_ECP_C) #error "MBEDTLS_ECDH_C defined, but not all prerequisites" #endif @@ -191,6 +196,10 @@ #error "MBEDTLS_HAVEGE_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_HKDF_C) && !defined(MBEDTLS_MD_C) +#error "MBEDTLS_HKDF_C defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_HMAC_DRBG_C) && !defined(MBEDTLS_MD_C) #error "MBEDTLS_HMAC_DRBG_C defined, but not all prerequisites" #endif diff --git a/thirdparty/mbedtls/include/mbedtls/cipher.h b/thirdparty/mbedtls/include/mbedtls/cipher.h index 46b3bdfefa..ea0ce983f1 100644 --- a/thirdparty/mbedtls/include/mbedtls/cipher.h +++ b/thirdparty/mbedtls/include/mbedtls/cipher.h @@ -37,7 +37,7 @@ #include <stddef.h> -#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) #define MBEDTLS_CIPHER_MODE_AEAD #endif @@ -45,7 +45,7 @@ #define MBEDTLS_CIPHER_MODE_WITH_PADDING #endif -#if defined(MBEDTLS_ARC4_C) +#if defined(MBEDTLS_ARC4_C) || defined(MBEDTLS_CIPHER_NULL_CIPHER) #define MBEDTLS_CIPHER_MODE_STREAM #endif @@ -87,6 +87,7 @@ typedef enum { MBEDTLS_CIPHER_ID_BLOWFISH, /**< The Blowfish cipher. */ MBEDTLS_CIPHER_ID_ARC4, /**< The RC4 cipher. */ MBEDTLS_CIPHER_ID_ARIA, /**< The Aria cipher. */ + MBEDTLS_CIPHER_ID_CHACHA20, /**< The ChaCha20 cipher. */ } mbedtls_cipher_id_t; /** @@ -164,6 +165,13 @@ typedef enum { MBEDTLS_CIPHER_ARIA_128_CCM, /**< Aria cipher with 128-bit key and CCM mode. */ MBEDTLS_CIPHER_ARIA_192_CCM, /**< Aria cipher with 192-bit key and CCM mode. */ MBEDTLS_CIPHER_ARIA_256_CCM, /**< Aria cipher with 256-bit key and CCM mode. */ + MBEDTLS_CIPHER_AES_128_OFB, /**< AES 128-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_192_OFB, /**< AES 192-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_256_OFB, /**< AES 256-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_128_XTS, /**< AES 128-bit cipher in XTS block mode. */ + MBEDTLS_CIPHER_AES_256_XTS, /**< AES 256-bit cipher in XTS block mode. */ + MBEDTLS_CIPHER_CHACHA20, /**< ChaCha20 stream cipher. */ + MBEDTLS_CIPHER_CHACHA20_POLY1305, /**< ChaCha20-Poly1305 AEAD cipher. */ } mbedtls_cipher_type_t; /** Supported cipher modes. */ @@ -172,11 +180,13 @@ typedef enum { MBEDTLS_MODE_ECB, /**< The ECB cipher mode. */ MBEDTLS_MODE_CBC, /**< The CBC cipher mode. */ MBEDTLS_MODE_CFB, /**< The CFB cipher mode. */ - MBEDTLS_MODE_OFB, /**< The OFB cipher mode - unsupported. */ + MBEDTLS_MODE_OFB, /**< The OFB cipher mode. */ MBEDTLS_MODE_CTR, /**< The CTR cipher mode. */ MBEDTLS_MODE_GCM, /**< The GCM cipher mode. */ MBEDTLS_MODE_STREAM, /**< The stream cipher mode. */ MBEDTLS_MODE_CCM, /**< The CCM cipher mode. */ + MBEDTLS_MODE_XTS, /**< The XTS cipher mode. */ + MBEDTLS_MODE_CHACHAPOLY, /**< The ChaCha-Poly cipher mode. */ } mbedtls_cipher_mode_t; /** Supported cipher padding types. */ @@ -292,7 +302,8 @@ typedef struct { /** Number of Bytes that have not been processed yet. */ size_t unprocessed_len; - /** Current IV or NONCE_COUNTER for CTR-mode. */ + /** Current IV or NONCE_COUNTER for CTR-mode, data unit (or sector) number + * for XTS-mode. */ unsigned char iv[MBEDTLS_MAX_IV_LENGTH]; /** IV size in Bytes, for ciphers with variable-length IVs. */ @@ -579,11 +590,11 @@ int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx, */ int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx ); -#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) /** * \brief This function adds additional data for AEAD ciphers. - * Only supported with GCM. Must be called - * exactly once, after mbedtls_cipher_reset(). + * Currently supported with GCM and ChaCha20+Poly1305. + * Must be called exactly once, after mbedtls_cipher_reset(). * * \param ctx The generic cipher context. * \param ad The additional data to use. @@ -594,7 +605,7 @@ int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx ); */ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx, const unsigned char *ad, size_t ad_len ); -#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ /** * \brief The generic cipher update function. It encrypts or @@ -652,10 +663,10 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx, unsigned char *output, size_t *olen ); -#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) /** * \brief This function writes a tag for AEAD ciphers. - * Only supported with GCM. + * Currently supported with GCM and ChaCha20+Poly1305. * Must be called after mbedtls_cipher_finish(). * * \param ctx The generic cipher context. @@ -670,7 +681,7 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, /** * \brief This function checks the tag for AEAD ciphers. - * Only supported with GCM. + * Currently supported with GCM and ChaCha20+Poly1305. * Must be called after mbedtls_cipher_finish(). * * \param ctx The generic cipher context. @@ -682,7 +693,7 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, */ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx, const unsigned char *tag, size_t tag_len ); -#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ /** * \brief The generic all-in-one encryption/decryption function, diff --git a/thirdparty/mbedtls/include/mbedtls/cipher_internal.h b/thirdparty/mbedtls/include/mbedtls/cipher_internal.h index 969ff9ccb8..c6def0bef7 100644 --- a/thirdparty/mbedtls/include/mbedtls/cipher_internal.h +++ b/thirdparty/mbedtls/include/mbedtls/cipher_internal.h @@ -64,6 +64,14 @@ struct mbedtls_cipher_base_t unsigned char *output ); #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + /** Encrypt using OFB (Full length) */ + int (*ofb_func)( void *ctx, size_t length, size_t *iv_off, + unsigned char *iv, + const unsigned char *input, + unsigned char *output ); +#endif + #if defined(MBEDTLS_CIPHER_MODE_CTR) /** Encrypt using CTR */ int (*ctr_func)( void *ctx, size_t length, size_t *nc_off, @@ -71,6 +79,13 @@ struct mbedtls_cipher_base_t const unsigned char *input, unsigned char *output ); #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + /** Encrypt or decrypt using XTS. */ + int (*xts_func)( void *ctx, mbedtls_operation_t mode, size_t length, + const unsigned char data_unit[16], + const unsigned char *input, unsigned char *output ); +#endif + #if defined(MBEDTLS_CIPHER_MODE_STREAM) /** Encrypt using STREAM */ int (*stream_func)( void *ctx, size_t length, diff --git a/thirdparty/mbedtls/include/mbedtls/cmac.h b/thirdparty/mbedtls/include/mbedtls/cmac.h index 913c05f8a7..a4fd552565 100644 --- a/thirdparty/mbedtls/include/mbedtls/cmac.h +++ b/thirdparty/mbedtls/include/mbedtls/cmac.h @@ -28,7 +28,7 @@ #ifndef MBEDTLS_CMAC_H #define MBEDTLS_CMAC_H -#include "mbedtls/cipher.h" +#include "cipher.h" #ifdef __cplusplus extern "C" { diff --git a/thirdparty/mbedtls/include/mbedtls/compat-1.3.h b/thirdparty/mbedtls/include/mbedtls/compat-1.3.h index 600a0f154c..213b691403 100644 --- a/thirdparty/mbedtls/include/mbedtls/compat-1.3.h +++ b/thirdparty/mbedtls/include/mbedtls/compat-1.3.h @@ -1378,7 +1378,8 @@ #define SSL_ANTI_REPLAY_ENABLED MBEDTLS_SSL_ANTI_REPLAY_ENABLED #define SSL_ARC4_DISABLED MBEDTLS_SSL_ARC4_DISABLED #define SSL_ARC4_ENABLED MBEDTLS_SSL_ARC4_ENABLED -#define SSL_BUFFER_LEN MBEDTLS_SSL_BUFFER_LEN +#define SSL_BUFFER_LEN ( ( ( MBEDTLS_SSL_IN_BUFFER_LEN ) < ( MBEDTLS_SSL_OUT_BUFFER_LEN ) ) \ + ? ( MBEDTLS_SSL_IN_BUFFER_LEN ) : ( MBEDTLS_SSL_OUT_BUFFER_LEN ) ) #define SSL_CACHE_DEFAULT_MAX_ENTRIES MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES #define SSL_CACHE_DEFAULT_TIMEOUT MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT #define SSL_CBC_RECORD_SPLITTING_DISABLED MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h index ae10a4d728..70820be56f 100644 --- a/thirdparty/mbedtls/include/mbedtls/config.h +++ b/thirdparty/mbedtls/include/mbedtls/config.h @@ -8,7 +8,7 @@ * memory footprint. */ /* - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Copyright (C) 2006-2018, ARM Limited, All Rights Reserved * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -89,6 +89,28 @@ //#define MBEDTLS_NO_UDBL_DIVISION /** + * \def MBEDTLS_NO_64BIT_MULTIPLICATION + * + * The platform lacks support for 32x32 -> 64-bit multiplication. + * + * Used in: + * library/poly1305.c + * + * Some parts of the library may use multiplication of two unsigned 32-bit + * operands with a 64-bit result in order to speed up computations. On some + * platforms, this is not available in hardware and has to be implemented in + * software, usually in a library provided by the toolchain. + * + * Sometimes it is not desirable to have to link to that library. This option + * removes the dependency of that library on platforms that lack a hardware + * 64-bit multiplier by embedding a software implementation in Mbed TLS. + * + * Note that depending on the compiler, this may decrease performance compared + * to using the library function provided by the toolchain. + */ +//#define MBEDTLS_NO_64BIT_MULTIPLICATION + +/** * \def MBEDTLS_HAVE_SSE2 * * CPU supports SSE2 instruction set. @@ -279,14 +301,18 @@ //#define MBEDTLS_BLOWFISH_ALT //#define MBEDTLS_CAMELLIA_ALT //#define MBEDTLS_CCM_ALT +//#define MBEDTLS_CHACHA20_ALT +//#define MBEDTLS_CHACHAPOLY_ALT //#define MBEDTLS_CMAC_ALT //#define MBEDTLS_DES_ALT //#define MBEDTLS_DHM_ALT //#define MBEDTLS_ECJPAKE_ALT //#define MBEDTLS_GCM_ALT +//#define MBEDTLS_NIST_KW_ALT //#define MBEDTLS_MD2_ALT //#define MBEDTLS_MD4_ALT //#define MBEDTLS_MD5_ALT +//#define MBEDTLS_POLY1305_ALT //#define MBEDTLS_RIPEMD160_ALT //#define MBEDTLS_RSA_ALT //#define MBEDTLS_SHA1_ALT @@ -516,6 +542,20 @@ #define MBEDTLS_CIPHER_MODE_CTR /** + * \def MBEDTLS_CIPHER_MODE_OFB + * + * Enable Output Feedback mode (OFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_OFB + +/** + * \def MBEDTLS_CIPHER_MODE_XTS + * + * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES. + */ +#define MBEDTLS_CIPHER_MODE_XTS + +/** * \def MBEDTLS_CIPHER_NULL_CIPHER * * Enable NULL cipher. @@ -1135,6 +1175,17 @@ #define MBEDTLS_SSL_ALL_ALERT_MESSAGES /** + * \def MBEDTLS_SSL_ASYNC_PRIVATE + * + * Enable asynchronous external private key operations in SSL. This allows + * you to configure an SSL connection to call an external cryptographic + * module to perform private key operations instead of performing the + * operation inside the library. + * + */ +//#define MBEDTLS_SSL_ASYNC_PRIVATE + +/** * \def MBEDTLS_SSL_DEBUG_ALL * * Enable the debug messages in SSL module for all issues. @@ -1920,6 +1971,26 @@ #define MBEDTLS_CERTS_C /** + * \def MBEDTLS_CHACHA20_C + * + * Enable the ChaCha20 stream cipher. + * + * Module: library/chacha20.c + */ +#define MBEDTLS_CHACHA20_C + +/** + * \def MBEDTLS_CHACHAPOLY_C + * + * Enable the ChaCha20-Poly1305 AEAD algorithm. + * + * Module: library/chachapoly.c + * + * This module requires: MBEDTLS_CHACHA20_C, MBEDTLS_POLY1305_C + */ +#define MBEDTLS_CHACHAPOLY_C + +/** * \def MBEDTLS_CIPHER_C * * Enable the generic cipher layer. @@ -2150,6 +2221,21 @@ //#define MBEDTLS_HAVEGE_C /** + * \def MBEDTLS_HKDF_C + * + * Enable the HKDF algorithm (RFC 5869). + * + * Module: library/hkdf.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the Hashed Message Authentication Code + * (HMAC)-based key derivation function (HKDF). + */ +#define MBEDTLS_HKDF_C + +/** * \def MBEDTLS_HMAC_DRBG_C * * Enable the HMAC_DRBG random generator. @@ -2164,6 +2250,19 @@ #define MBEDTLS_HMAC_DRBG_C /** + * \def MBEDTLS_NIST_KW_C + * + * Enable the Key Wrapping mode for 128-bit block ciphers, + * as defined in NIST SP 800-38F. Only KW and KWP modes + * are supported. At the moment, only AES is approved by NIST. + * + * Module: library/nist_kw.c + * + * Requires: MBEDTLS_AES_C and MBEDTLS_CIPHER_C + */ +//#define MBEDTLS_NIST_KW_C + +/** * \def MBEDTLS_MD_C * * Enable the generic message digest layer. @@ -2447,6 +2546,16 @@ #define MBEDTLS_PLATFORM_C /** + * \def MBEDTLS_POLY1305_C + * + * Enable the Poly1305 MAC algorithm. + * + * Module: library/poly1305.c + * Caller: library/chachapoly.c + */ +#define MBEDTLS_POLY1305_C + +/** * \def MBEDTLS_RIPEMD160_C * * Enable the RIPEMD-160 hash algorithm. @@ -2856,7 +2965,51 @@ //#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ /* SSL options */ -//#define MBEDTLS_SSL_MAX_CONTENT_LEN 16384 /**< Maxium fragment length in bytes, determines the size of each of the two internal I/O buffers */ + +/** \def MBEDTLS_SSL_MAX_CONTENT_LEN + * + * Maximum fragment length in bytes. + * + * Determines the size of both the incoming and outgoing TLS I/O buffers. + * + * Uncommenting MBEDTLS_SSL_IN_CONTENT_LEN and/or MBEDTLS_SSL_OUT_CONTENT_LEN + * will override this length by setting maximum incoming and/or outgoing + * fragment length, respectively. + */ +//#define MBEDTLS_SSL_MAX_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_IN_CONTENT_LEN + * + * Maximum incoming fragment length in bytes. + * + * Uncomment to set the size of the inward TLS buffer independently of the + * outward buffer. + */ +//#define MBEDTLS_SSL_IN_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_OUT_CONTENT_LEN + * + * Maximum outgoing fragment length in bytes. + * + * Uncomment to set the size of the outward TLS buffer independently of the + * inward buffer. + * + * It is possible to save RAM by setting a smaller outward buffer, while keeping + * the default inward 16384 byte buffer to conform to the TLS specification. + * + * The minimum required outward buffer size is determined by the handshake + * protocol's usage. Handshaking will fail if the outward buffer is too small. + * The specific size requirement depends on the configured ciphers and any + * certificate data which is sent during the handshake. + * + * For absolute minimum RAM usage, it's best to enable + * MBEDTLS_SSL_MAX_FRAGMENT_LENGTH and reduce MBEDTLS_SSL_MAX_CONTENT_LEN. This + * reduces both incoming and outgoing buffer sizes. However this is only + * guaranteed if the other end of the connection also supports the TLS + * max_fragment_len extension. Otherwise the connection may fail. + */ +//#define MBEDTLS_SSL_OUT_CONTENT_LEN 16384 + //#define MBEDTLS_SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ //#define MBEDTLS_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */ //#define MBEDTLS_SSL_COOKIE_TIMEOUT 60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */ @@ -2933,7 +3086,7 @@ /* \} name SECTION: Customisation configuration options */ /* Target and application specific configurations */ -//#define YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE "mbedtls/target_config.h" +//#define YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE "target_config.h" #if defined(TARGET_LIKE_MBED) && defined(YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE) #include YOTTA_CFG_MBEDTLS_TARGET_CONFIG_FILE diff --git a/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h b/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h index dcbc047924..3835d7299b 100644 --- a/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h +++ b/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h @@ -36,7 +36,7 @@ #include "aes.h" #if defined(MBEDTLS_THREADING_C) -#include "mbedtls/threading.h" +#include "threading.h" #endif #define MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED -0x0034 /**< The entropy source failed. */ diff --git a/thirdparty/mbedtls/include/mbedtls/error.h b/thirdparty/mbedtls/include/mbedtls/error.h index a17f8d8ace..6b82d4fbbe 100644 --- a/thirdparty/mbedtls/include/mbedtls/error.h +++ b/thirdparty/mbedtls/include/mbedtls/error.h @@ -4,7 +4,7 @@ * \brief Error to string translation */ /* - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * Copyright (C) 2006-2018, ARM Limited, All Rights Reserved * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -62,7 +62,7 @@ * DES 2 0x0032-0x0032 0x0033-0x0033 * CTR_DBRG 4 0x0034-0x003A * ENTROPY 3 0x003C-0x0040 0x003D-0x003F - * NET 11 0x0042-0x0052 0x0043-0x0045 + * NET 13 0x0042-0x0052 0x0043-0x0049 * ARIA 4 0x0058-0x005E * ASN1 7 0x0060-0x006C * CMAC 1 0x007A-0x007A @@ -77,6 +77,9 @@ * SHA1 1 0x0035-0x0035 * SHA256 1 0x0037-0x0037 * SHA512 1 0x0039-0x0039 + * CHACHA20 3 0x0051-0x0055 + * POLY1305 3 0x0057-0x005B + * CHACHAPOLY 2 0x0054-0x0056 * * High-level module nr (3 bits - 0x0...-0x7...) * Name ID Nr of Errors @@ -89,8 +92,9 @@ * RSA 4 11 * ECP 4 9 (Started from top) * MD 5 5 + * HKDF 5 1 (Started from top) * CIPHER 6 8 - * SSL 6 17 (Started from top) + * SSL 6 22 (Started from top) * SSL 7 31 * * Module dependent error code (5 bits 0x.00.-0x.F8.) diff --git a/thirdparty/mbedtls/include/mbedtls/gcm.h b/thirdparty/mbedtls/include/mbedtls/gcm.h index bec5577142..87535ab957 100644 --- a/thirdparty/mbedtls/include/mbedtls/gcm.h +++ b/thirdparty/mbedtls/include/mbedtls/gcm.h @@ -113,21 +113,41 @@ int mbedtls_gcm_setkey( mbedtls_gcm_context *ctx, * the same as input buffer. If the buffers overlap, the output * buffer must trail at least 8 Bytes behind the input buffer. * + * \warning When this function performs a decryption, it outputs the + * authentication tag and does not verify that the data is + * authentic. You should use this function to perform encryption + * only. For decryption, use mbedtls_gcm_auth_decrypt() instead. + * * \param ctx The GCM context to use for encryption or decryption. - * \param mode The operation to perform: #MBEDTLS_GCM_ENCRYPT or - * #MBEDTLS_GCM_DECRYPT. - * \param length The length of the input data. This must be a multiple of - * 16 except in the last call before mbedtls_gcm_finish(). + * \param mode The operation to perform: + * - #MBEDTLS_GCM_ENCRYPT to perform authenticated encryption. + * The ciphertext is written to \p output and the + * authentication tag is written to \p tag. + * - #MBEDTLS_GCM_DECRYPT to perform decryption. + * The plaintext is written to \p output and the + * authentication tag is written to \p tag. + * Note that this mode is not recommended, because it does + * not verify the authenticity of the data. For this reason, + * you should use mbedtls_gcm_auth_decrypt() instead of + * calling this function in decryption mode. + * \param length The length of the input data, which is equal to the length + * of the output data. * \param iv The initialization vector. * \param iv_len The length of the IV. * \param add The buffer holding the additional data. * \param add_len The length of the additional data. - * \param input The buffer holding the input data. - * \param output The buffer for holding the output data. + * \param input The buffer holding the input data. Its size is \b length. + * \param output The buffer for holding the output data. It must have room + * for \b length bytes. * \param tag_len The length of the tag to generate. * \param tag The buffer for holding the tag. * - * \return \c 0 on success. + * \return \c 0 if the encryption or decryption was performed + * successfully. Note that in #MBEDTLS_GCM_DECRYPT mode, + * this does not indicate that the data is authentic. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT if the lengths are not valid. + * \return #MBEDTLS_ERR_GCM_HW_ACCEL_FAILED or a cipher-specific + * error code if the encryption or decryption failed. */ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, int mode, @@ -150,19 +170,23 @@ int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, * must trail at least 8 Bytes behind the input buffer. * * \param ctx The GCM context. - * \param length The length of the input data. This must be a multiple - * of 16 except in the last call before mbedtls_gcm_finish(). + * \param length The length of the ciphertext to decrypt, which is also + * the length of the decrypted plaintext. * \param iv The initialization vector. * \param iv_len The length of the IV. * \param add The buffer holding the additional data. * \param add_len The length of the additional data. - * \param tag The buffer holding the tag. - * \param tag_len The length of the tag. - * \param input The buffer holding the input data. - * \param output The buffer for holding the output data. + * \param tag The buffer holding the tag to verify. + * \param tag_len The length of the tag to verify. + * \param input The buffer holding the ciphertext. Its size is \b length. + * \param output The buffer for holding the decrypted plaintext. It must + * have room for \b length bytes. * - * \return 0 if successful and authenticated. - * \return #MBEDTLS_ERR_GCM_AUTH_FAILED if the tag does not match. + * \return \c 0 if successful and authenticated. + * \return #MBEDTLS_ERR_GCM_AUTH_FAILED if the tag does not match. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT if the lengths are not valid. + * \return #MBEDTLS_ERR_GCM_HW_ACCEL_FAILED or a cipher-specific + * error code if the decryption failed. */ int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx, size_t length, diff --git a/thirdparty/mbedtls/include/mbedtls/hkdf.h b/thirdparty/mbedtls/include/mbedtls/hkdf.h new file mode 100644 index 0000000000..6833e7272e --- /dev/null +++ b/thirdparty/mbedtls/include/mbedtls/hkdf.h @@ -0,0 +1,125 @@ +/** + * \file hkdf.h + * + * \brief This file contains the HKDF interface. + * + * The HMAC-based Extract-and-Expand Key Derivation Function (HKDF) is + * specified by RFC 5869. + */ +/* + * Copyright (C) 2016-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef MBEDTLS_HKDF_H +#define MBEDTLS_HKDF_H + +#include "md.h" + +/** + * \name HKDF Error codes + * \{ + */ +#define MBEDTLS_ERR_HKDF_BAD_INPUT_DATA -0x5F80 /**< Bad input parameters to function. */ +/* \} name */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief This is the HMAC-based Extract-and-Expand Key Derivation Function + * (HKDF). + * + * \param md A hash function; md.size denotes the length of the hash + * function output in bytes. + * \param salt An optional salt value (a non-secret random value); + * if the salt is not provided, a string of all zeros of + * md.size length is used as the salt. + * \param salt_len The length in bytes of the optional \p salt. + * \param ikm The input keying material. + * \param ikm_len The length in bytes of \p ikm. + * \param info An optional context and application specific information + * string. This can be a zero-length string. + * \param info_len The length of \p info in bytes. + * \param okm The output keying material of \p okm_len bytes. + * \param okm_len The length of the output keying material in bytes. This + * must be less than or equal to 255 * md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf( const mbedtls_md_info_t *md, const unsigned char *salt, + size_t salt_len, const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len ); + +/** + * \brief Take the input keying material \p ikm and extract from it a + * fixed-length pseudorandom key \p prk. + * + * \param md A hash function; md.size denotes the length of the + * hash function output in bytes. + * \param salt An optional salt value (a non-secret random value); + * if the salt is not provided, a string of all zeros + * of md.size length is used as the salt. + * \param salt_len The length in bytes of the optional \p salt. + * \param ikm The input keying material. + * \param ikm_len The length in bytes of \p ikm. + * \param[out] prk A pseudorandom key of at least md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf_extract( const mbedtls_md_info_t *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk ); + +/** + * \brief Expand the supplied \p prk into several additional pseudorandom + * keys, which is the output of the HKDF. + * + * \param md A hash function; md.size denotes the length of the hash + * function output in bytes. + * \param prk A pseudorandom key of at least md.size bytes. \p prk is usually, + * the output from the HKDF extract step. + * \param prk_len The length in bytes of \p prk. + * \param info An optional context and application specific information + * string. This can be a zero-length string. + * \param info_len The length of \p info in bytes. + * \param okm The output keying material of \p okm_len bytes. + * \param okm_len The length of the output keying material in bytes. This + * must be less than or equal to 255 * md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf_expand( const mbedtls_md_info_t *md, const unsigned char *prk, + size_t prk_len, const unsigned char *info, + size_t info_len, unsigned char *okm, size_t okm_len ); + +#ifdef __cplusplus +} +#endif + +#endif /* hkdf.h */ diff --git a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h index e0821cf788..2608de8595 100644 --- a/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h +++ b/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h @@ -27,7 +27,7 @@ #include "md.h" #if defined(MBEDTLS_THREADING_C) -#include "mbedtls/threading.h" +#include "threading.h" #endif /* diff --git a/thirdparty/mbedtls/include/mbedtls/net.h b/thirdparty/mbedtls/include/mbedtls/net.h index 28ae8217c0..6c13b53fb9 100644 --- a/thirdparty/mbedtls/include/mbedtls/net.h +++ b/thirdparty/mbedtls/include/mbedtls/net.h @@ -1,7 +1,7 @@ /** * \file net.h * - * \brief Deprecated header file that includes mbedtls/net_sockets.h + * \brief Deprecated header file that includes net_sockets.h * * \deprecated Superseded by mbedtls/net_sockets.h */ @@ -25,7 +25,7 @@ */ #if !defined(MBEDTLS_DEPRECATED_REMOVED) -#include "mbedtls/net_sockets.h" +#include "net_sockets.h" #if defined(MBEDTLS_DEPRECATED_WARNING) #warning "Deprecated header file: Superseded by mbedtls/net_sockets.h" #endif /* MBEDTLS_DEPRECATED_WARNING */ diff --git a/thirdparty/mbedtls/include/mbedtls/net_sockets.h b/thirdparty/mbedtls/include/mbedtls/net_sockets.h index 0f9b31ebcb..9f07eeb4d3 100644 --- a/thirdparty/mbedtls/include/mbedtls/net_sockets.h +++ b/thirdparty/mbedtls/include/mbedtls/net_sockets.h @@ -1,7 +1,23 @@ /** * \file net_sockets.h * - * \brief Network communication functions + * \brief Network sockets abstraction layer to integrate Mbed TLS into a + * BSD-style sockets API. + * + * The network sockets module provides an example integration of the + * Mbed TLS library into a BSD sockets implementation. The module is + * intended to be an example of how Mbed TLS can be integrated into a + * networking stack, as well as to be Mbed TLS's network integration + * for its supported platforms. + * + * The module is intended only to be used with the Mbed TLS library and + * is not intended to be used by third party application software + * directly. + * + * The supported platforms are as follows: + * * Microsoft Windows and Windows CE + * * POSIX/Unix platforms including Linux, OS X + * */ /* * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved diff --git a/thirdparty/mbedtls/include/mbedtls/nist_kw.h b/thirdparty/mbedtls/include/mbedtls/nist_kw.h new file mode 100644 index 0000000000..5a0f656a8f --- /dev/null +++ b/thirdparty/mbedtls/include/mbedtls/nist_kw.h @@ -0,0 +1,178 @@ +/** + * \file nist_kw.h + * + * \brief This file provides an API for key wrapping (KW) and key wrapping with + * padding (KWP) as defined in NIST SP 800-38F. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf + * + * Key wrapping specifies a deterministic authenticated-encryption mode + * of operation, according to <em>NIST SP 800-38F: Recommendation for + * Block Cipher Modes of Operation: Methods for Key Wrapping</em>. Its + * purpose is to protect cryptographic keys. + * + * Its equivalent is RFC 3394 for KW, and RFC 5649 for KWP. + * https://tools.ietf.org/html/rfc3394 + * https://tools.ietf.org/html/rfc5649 + * + */ +/* + * Copyright (C) 2018, Arm Limited (or its affiliates), All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_NIST_KW_H +#define MBEDTLS_NIST_KW_H + +#include "cipher.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + MBEDTLS_KW_MODE_KW = 0, + MBEDTLS_KW_MODE_KWP = 1 +} mbedtls_nist_kw_mode_t; + +#if !defined(MBEDTLS_NIST_KW_ALT) +// Regular implementation +// + +/** + * \brief The key wrapping context-type definition. The key wrapping context is passed + * to the APIs called. + * + * \note The definition of this type may change in future library versions. + * Don't make any assumptions on this context! + */ +typedef struct { + mbedtls_cipher_context_t cipher_ctx; /*!< The cipher context used. */ +} mbedtls_nist_kw_context; + +#else /* MBEDTLS_NIST_key wrapping_ALT */ +#include "nist_kw_alt.h" +#endif /* MBEDTLS_NIST_KW_ALT */ + +/** + * \brief This function initializes the specified key wrapping context + * to make references valid and prepare the context + * for mbedtls_nist_kw_setkey() or mbedtls_nist_kw_free(). + * + * \param ctx The key wrapping context to initialize. + * + */ +void mbedtls_nist_kw_init( mbedtls_nist_kw_context *ctx ); + +/** + * \brief This function initializes the key wrapping context set in the + * \p ctx parameter and sets the encryption key. + * + * \param ctx The key wrapping context. + * \param cipher The 128-bit block cipher to use. Only AES is supported. + * \param key The Key Encryption Key (KEK). + * \param keybits The KEK size in bits. This must be acceptable by the cipher. + * \param is_wrap Specify whether the operation within the context is wrapping or unwrapping + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for any invalid input. + * \return \c MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE for 128-bit block ciphers + * which are not supported. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_setkey( mbedtls_nist_kw_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits, + const int is_wrap ); + +/** + * \brief This function releases and clears the specified key wrapping context + * and underlying cipher sub-context. + * + * \param ctx The key wrapping context to clear. + */ +void mbedtls_nist_kw_free( mbedtls_nist_kw_context *ctx ); + +/** + * \brief This function encrypts a buffer using key wrapping. + * + * \param ctx The key wrapping context to use for encryption. + * \param mode The key wrapping mode to use (MBEDTLS_KW_MODE_KW or MBEDTLS_KW_MODE_KWP) + * \param input The buffer holding the input data. + * \param in_len The length of the input data in Bytes. + * The input uses units of 8 Bytes called semiblocks. + * <ul><li>For KW mode: a multiple of 8 bytes between 16 and 2^57-8 inclusive. </li> + * <li>For KWP mode: any length between 1 and 2^32-1 inclusive.</li></ul> + * \param[out] output The buffer holding the output data. + * <ul><li>For KW mode: Must be at least 8 bytes larger than \p in_len.</li> + * <li>For KWP mode: Must be at least 8 bytes larger rounded up to a multiple of + * 8 bytes for KWP (15 bytes at most).</li></ul> + * \param[out] out_len The number of bytes written to the output buffer. \c 0 on failure. + * \param[in] out_size The capacity of the output buffer. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for invalid input length. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_wrap( mbedtls_nist_kw_context *ctx, mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t* out_len, size_t out_size ); + +/** + * \brief This function decrypts a buffer using key wrapping. + * + * \param ctx The key wrapping context to use for decryption. + * \param mode The key wrapping mode to use (MBEDTLS_KW_MODE_KW or MBEDTLS_KW_MODE_KWP) + * \param input The buffer holding the input data. + * \param in_len The length of the input data in Bytes. + * The input uses units of 8 Bytes called semiblocks. + * The input must be a multiple of semiblocks. + * <ul><li>For KW mode: a multiple of 8 bytes between 24 and 2^57 inclusive. </li> + * <li>For KWP mode: a multiple of 8 bytes between 16 and 2^32 inclusive.</li></ul> + * \param[out] output The buffer holding the output data. + * The output buffer's minimal length is 8 bytes shorter than \p in_len. + * \param[out] out_len The number of bytes written to the output buffer. \c 0 on failure. + * For KWP mode, the length could be up to 15 bytes shorter than \p in_len, + * depending on how much padding was added to the data. + * \param[in] out_size The capacity of the output buffer. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for invalid input length. + * \return \c MBEDTLS_ERR_CIPHER_AUTH_FAILED for verification failure of the ciphertext. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_unwrap( mbedtls_nist_kw_context *ctx, mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t* out_len, size_t out_size); + + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +/** + * \brief The key wrapping checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_nist_kw_self_test( int verbose ); +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_NIST_KW_H */ diff --git a/thirdparty/mbedtls/include/mbedtls/oid.h b/thirdparty/mbedtls/include/mbedtls/oid.h index 408645ece7..f82554844c 100644 --- a/thirdparty/mbedtls/include/mbedtls/oid.h +++ b/thirdparty/mbedtls/include/mbedtls/oid.h @@ -97,6 +97,8 @@ /* ISO arc for standard certificate and CRL extensions */ #define MBEDTLS_OID_ID_CE MBEDTLS_OID_ISO_CCITT_DS "\x1D" /**< id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} */ +#define MBEDTLS_OID_NIST_ALG MBEDTLS_OID_GOV "\x03\x04" /** { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) */ + /** * Private Internet Extensions * { iso(1) identified-organization(3) dod(6) internet(1) @@ -219,12 +221,12 @@ #define MBEDTLS_OID_DIGEST_ALG_MD4 MBEDTLS_OID_RSA_COMPANY "\x02\x04" /**< id-mbedtls_md4 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 4 } */ #define MBEDTLS_OID_DIGEST_ALG_MD5 MBEDTLS_OID_RSA_COMPANY "\x02\x05" /**< id-mbedtls_md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } */ #define MBEDTLS_OID_DIGEST_ALG_SHA1 MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_SHA1 /**< id-mbedtls_sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } */ -#define MBEDTLS_OID_DIGEST_ALG_SHA224 MBEDTLS_OID_GOV "\x03\x04\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */ -#define MBEDTLS_OID_DIGEST_ALG_SHA256 MBEDTLS_OID_GOV "\x03\x04\x02\x01" /**< id-mbedtls_sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA224 MBEDTLS_OID_NIST_ALG "\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA256 MBEDTLS_OID_NIST_ALG "\x02\x01" /**< id-mbedtls_sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */ -#define MBEDTLS_OID_DIGEST_ALG_SHA384 MBEDTLS_OID_GOV "\x03\x04\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA384 MBEDTLS_OID_NIST_ALG "\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */ -#define MBEDTLS_OID_DIGEST_ALG_SHA512 MBEDTLS_OID_GOV "\x03\x04\x02\x03" /**< id-mbedtls_sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA512 MBEDTLS_OID_NIST_ALG "\x02\x03" /**< id-mbedtls_sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */ #define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */ @@ -241,8 +243,21 @@ */ #define MBEDTLS_OID_DES_CBC MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_OIW_SECSIG_ALG "\x07" /**< desCBC OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 7 } */ #define MBEDTLS_OID_DES_EDE3_CBC MBEDTLS_OID_RSA_COMPANY "\x03\x07" /**< des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) -- us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } */ +#define MBEDTLS_OID_AES MBEDTLS_OID_NIST_ALG "\x01" /** aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) 1 } */ /* + * Key Wrapping algorithms + */ +/* + * RFC 5649 + */ +#define MBEDTLS_OID_AES128_KW MBEDTLS_OID_AES "\x05" /** id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } */ +#define MBEDTLS_OID_AES128_KWP MBEDTLS_OID_AES "\x08" /** id-aes128-wrap-pad OBJECT IDENTIFIER ::= { aes 8 } */ +#define MBEDTLS_OID_AES192_KW MBEDTLS_OID_AES "\x19" /** id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } */ +#define MBEDTLS_OID_AES192_KWP MBEDTLS_OID_AES "\x1c" /** id-aes192-wrap-pad OBJECT IDENTIFIER ::= { aes 28 } */ +#define MBEDTLS_OID_AES256_KW MBEDTLS_OID_AES "\x2d" /** id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } */ +#define MBEDTLS_OID_AES256_KWP MBEDTLS_OID_AES "\x30" /** id-aes256-wrap-pad OBJECT IDENTIFIER ::= { aes 48 } */ +/* * PKCS#5 OIDs */ #define MBEDTLS_OID_PKCS5_PBKDF2 MBEDTLS_OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */ diff --git a/thirdparty/mbedtls/include/mbedtls/platform.h b/thirdparty/mbedtls/include/mbedtls/platform.h index bba770911e..624cc642ac 100644 --- a/thirdparty/mbedtls/include/mbedtls/platform.h +++ b/thirdparty/mbedtls/include/mbedtls/platform.h @@ -40,7 +40,7 @@ #endif #if defined(MBEDTLS_HAVE_TIME) -#include "mbedtls/platform_time.h" +#include "platform_time.h" #endif #ifdef __cplusplus @@ -121,8 +121,8 @@ extern "C" { #else /* For size_t */ #include <stddef.h> -extern void * (*mbedtls_calloc)( size_t n, size_t size ); -extern void (*mbedtls_free)( void *ptr ); +extern void *mbedtls_calloc( size_t n, size_t size ); +extern void mbedtls_free( void *ptr ); /** * \brief This function dynamically sets the memory-management diff --git a/thirdparty/mbedtls/include/mbedtls/poly1305.h b/thirdparty/mbedtls/include/mbedtls/poly1305.h new file mode 100644 index 0000000000..54b50abc25 --- /dev/null +++ b/thirdparty/mbedtls/include/mbedtls/poly1305.h @@ -0,0 +1,181 @@ +/** + * \file poly1305.h + * + * \brief This file contains Poly1305 definitions and functions. + * + * Poly1305 is a one-time message authenticator that can be used to + * authenticate messages. Poly1305-AES was created by Daniel + * Bernstein https://cr.yp.to/mac/poly1305-20050329.pdf The generic + * Poly1305 algorithm (not tied to AES) was also standardized in RFC + * 7539. + * + * \author Daniel King <damaki.gh@gmail.com> + */ + +/* Copyright (C) 2006-2018, Arm Limited (or its affiliates), All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_POLY1305_H +#define MBEDTLS_POLY1305_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include <stdint.h> +#include <stddef.h> + +#define MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA -0x0057 /**< Invalid input parameter(s). */ +#define MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE -0x0059 /**< Feature not available. For example, s part of the API is not implemented. */ +#define MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED -0x005B /**< Poly1305 hardware accelerator failed. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_POLY1305_ALT) + +typedef struct +{ + uint32_t r[4]; /** The value for 'r' (low 128 bits of the key). */ + uint32_t s[4]; /** The value for 's' (high 128 bits of the key). */ + uint32_t acc[5]; /** The accumulator number. */ + uint8_t queue[16]; /** The current partial block of data. */ + size_t queue_len; /** The number of bytes stored in 'queue'. */ +} +mbedtls_poly1305_context; + +#else /* MBEDTLS_POLY1305_ALT */ +#include "poly1305_alt.h" +#endif /* MBEDTLS_POLY1305_ALT */ + +/** + * \brief This function initializes the specified Poly1305 context. + * + * It must be the first API called before using + * the context. + * + * It is usually followed by a call to + * \c mbedtls_poly1305_starts(), then one or more calls to + * \c mbedtls_poly1305_update(), then one call to + * \c mbedtls_poly1305_finish(), then finally + * \c mbedtls_poly1305_free(). + * + * \param ctx The Poly1305 context to initialize. + */ +void mbedtls_poly1305_init( mbedtls_poly1305_context *ctx ); + +/** + * \brief This function releases and clears the specified Poly1305 context. + * + * \param ctx The Poly1305 context to clear. + */ +void mbedtls_poly1305_free( mbedtls_poly1305_context *ctx ); + +/** + * \brief This function sets the one-time authentication key. + * + * \warning The key must be unique and unpredictable for each + * invocation of Poly1305. + * + * \param ctx The Poly1305 context to which the key should be bound. + * \param key The buffer containing the 256-bit key. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if ctx or key are NULL. + */ +int mbedtls_poly1305_starts( mbedtls_poly1305_context *ctx, + const unsigned char key[32] ); + +/** + * \brief This functions feeds an input buffer into an ongoing + * Poly1305 computation. + * + * It is called between \c mbedtls_cipher_poly1305_starts() and + * \c mbedtls_cipher_poly1305_finish(). + * It can be called repeatedly to process a stream of data. + * + * \param ctx The Poly1305 context to use for the Poly1305 operation. + * \param ilen The length of the input data (in bytes). Any value is accepted. + * \param input The buffer holding the input data. + * This pointer can be NULL if ilen == 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if ctx or input are NULL. + */ +int mbedtls_poly1305_update( mbedtls_poly1305_context *ctx, + const unsigned char *input, + size_t ilen ); + +/** + * \brief This function generates the Poly1305 Message + * Authentication Code (MAC). + * + * \param ctx The Poly1305 context to use for the Poly1305 operation. + * \param mac The buffer to where the MAC is written. Must be big enough + * to hold the 16-byte MAC. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if ctx or mac are NULL. + */ +int mbedtls_poly1305_finish( mbedtls_poly1305_context *ctx, + unsigned char mac[16] ); + +/** + * \brief This function calculates the Poly1305 MAC of the input + * buffer with the provided key. + * + * \warning The key must be unique and unpredictable for each + * invocation of Poly1305. + * + * \param key The buffer containing the 256-bit key. + * \param ilen The length of the input data (in bytes). Any value is accepted. + * \param input The buffer holding the input data. + * This pointer can be NULL if ilen == 0. + * \param mac The buffer to where the MAC is written. Must be big enough + * to hold the 16-byte MAC. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if key, input, or mac are NULL. + */ +int mbedtls_poly1305_mac( const unsigned char key[32], + const unsigned char *input, + size_t ilen, + unsigned char mac[16] ); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The Poly1305 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_poly1305_self_test( int verbose ); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_POLY1305_H */ diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h index 250031a6d3..2d511a8ea1 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl.h @@ -62,7 +62,7 @@ #endif #if defined(MBEDTLS_HAVE_TIME) -#include "mbedtls/platform_time.h" +#include "platform_time.h" #endif /* @@ -120,6 +120,7 @@ #define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 /**< The alert message received indicates a non-fatal error. */ #define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600 /**< Couldn't set the hash for verifying CertificateVerify */ #define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580 /**< Internal-only message signaling that further message-processing should be done */ +#define MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS -0x6500 /**< The asynchronous operation is not completed yet. */ /* * Various constants @@ -219,7 +220,7 @@ #endif /* - * Maxium fragment length in bytes, + * Maximum fragment length in bytes, * determines the size of each of the two internal I/O buffers. * * Note: the RFC defines the default size of SSL / TLS messages. If you @@ -233,6 +234,14 @@ #define MBEDTLS_SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ #endif +#if !defined(MBEDTLS_SSL_IN_CONTENT_LEN) +#define MBEDTLS_SSL_IN_CONTENT_LEN MBEDTLS_SSL_MAX_CONTENT_LEN +#endif + +#if !defined(MBEDTLS_SSL_OUT_CONTENT_LEN) +#define MBEDTLS_SSL_OUT_CONTENT_LEN MBEDTLS_SSL_MAX_CONTENT_LEN +#endif + /* \} name SECTION: Module settings */ /* @@ -536,7 +545,6 @@ typedef void mbedtls_ssl_set_timer_t( void * ctx, */ typedef int mbedtls_ssl_get_timer_t( void * ctx ); - /* Defined below */ typedef struct mbedtls_ssl_session mbedtls_ssl_session; typedef struct mbedtls_ssl_context mbedtls_ssl_context; @@ -553,6 +561,218 @@ typedef struct mbedtls_ssl_key_cert mbedtls_ssl_key_cert; typedef struct mbedtls_ssl_flight_item mbedtls_ssl_flight_item; #endif +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Callback type: start external signature operation. + * + * This callback is called during an SSL handshake to start + * a signature decryption operation using an + * external processor. The parameter \p cert contains + * the public key; it is up to the callback function to + * determine how to access the associated private key. + * + * This function typically sends or enqueues a request, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * The parameters \p ssl and \p cert are guaranteed to remain + * valid throughout the handshake. On the other hand, this + * function must save the contents of \p hash if the value + * is needed for later processing, because the \p hash buffer + * is no longer valid after this function returns. + * + * This function may call mbedtls_ssl_set_async_operation_data() + * to store an operation context for later retrieval + * by the resume or cancel callback. + * + * \note For RSA signatures, this function must produce output + * that is consistent with PKCS#1 v1.5 in the same way as + * mbedtls_rsa_pkcs1_sign(). Before the private key operation, + * apply the padding steps described in RFC 8017, section 9.2 + * "EMSA-PKCS1-v1_5" as follows. + * - If \p md_alg is #MBEDTLS_MD_NONE, apply the PKCS#1 v1.5 + * encoding, treating \p hash as the DigestInfo to be + * padded. In other words, apply EMSA-PKCS1-v1_5 starting + * from step 3, with `T = hash` and `tLen = hash_len`. + * - If `md_alg != MBEDTLS_MD_NONE`, apply the PKCS#1 v1.5 + * encoding, treating \p hash as the hash to be encoded and + * padded. In other words, apply EMSA-PKCS1-v1_5 starting + * from step 2, with `digestAlgorithm` obtained by calling + * mbedtls_oid_get_oid_by_md() on \p md_alg. + * + * \note For ECDSA signatures, the output format is the DER encoding + * `Ecdsa-Sig-Value` defined in + * [RFC 4492 section 5.4](https://tools.ietf.org/html/rfc4492#section-5.4). + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param cert Certificate containing the public key. + * In simple cases, this is one of the pointers passed to + * mbedtls_ssl_conf_own_cert() when configuring the SSL + * connection. However, if other callbacks are used, this + * property may not hold. For example, if an SNI callback + * is registered with mbedtls_ssl_conf_sni(), then + * this callback determines what certificate is used. + * \param md_alg Hash algorithm. + * \param hash Buffer containing the hash. This buffer is + * no longer valid when the function returns. + * \param hash_len Size of the \c hash buffer in bytes. + * + * \return 0 if the operation was started successfully and the SSL + * stack should call the resume callback immediately. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * was started successfully and the SSL stack should return + * immediately without calling the resume callback yet. + * \return #MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH if the external + * processor does not support this key. The SSL stack will + * use the private key object instead. + * \return Any other error indicates a fatal failure and is + * propagated up the call chain. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and <b>must not</b> + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_sign_t( mbedtls_ssl_context *ssl, + mbedtls_x509_crt *cert, + mbedtls_md_type_t md_alg, + const unsigned char *hash, + size_t hash_len ); + +/** + * \brief Callback type: start external decryption operation. + * + * This callback is called during an SSL handshake to start + * an RSA decryption operation using an + * external processor. The parameter \p cert contains + * the public key; it is up to the callback function to + * determine how to access the associated private key. + * + * This function typically sends or enqueues a request, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * The parameters \p ssl and \p cert are guaranteed to remain + * valid throughout the handshake. On the other hand, this + * function must save the contents of \p input if the value + * is needed for later processing, because the \p input buffer + * is no longer valid after this function returns. + * + * This function may call mbedtls_ssl_set_async_operation_data() + * to store an operation context for later retrieval + * by the resume or cancel callback. + * + * \warning RSA decryption as used in TLS is subject to a potential + * timing side channel attack first discovered by Bleichenbacher + * in 1998. This attack can be remotely exploitable + * in practice. To avoid this attack, you must ensure that + * if the callback performs an RSA decryption, the time it + * takes to execute and return the result does not depend + * on whether the RSA decryption succeeded or reported + * invalid padding. + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param cert Certificate containing the public key. + * In simple cases, this is one of the pointers passed to + * mbedtls_ssl_conf_own_cert() when configuring the SSL + * connection. However, if other callbacks are used, this + * property may not hold. For example, if an SNI callback + * is registered with mbedtls_ssl_conf_sni(), then + * this callback determines what certificate is used. + * \param input Buffer containing the input ciphertext. This buffer + * is no longer valid when the function returns. + * \param input_len Size of the \p input buffer in bytes. + * + * \return 0 if the operation was started successfully and the SSL + * stack should call the resume callback immediately. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * was started successfully and the SSL stack should return + * immediately without calling the resume callback yet. + * \return #MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH if the external + * processor does not support this key. The SSL stack will + * use the private key object instead. + * \return Any other error indicates a fatal failure and is + * propagated up the call chain. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and <b>must not</b> + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_decrypt_t( mbedtls_ssl_context *ssl, + mbedtls_x509_crt *cert, + const unsigned char *input, + size_t input_len ); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/** + * \brief Callback type: resume external operation. + * + * This callback is called during an SSL handshake to resume + * an external operation started by the + * ::mbedtls_ssl_async_sign_t or + * ::mbedtls_ssl_async_decrypt_t callback. + * + * This function typically checks the status of a pending + * request or causes the request queue to make progress, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * This function may call mbedtls_ssl_get_async_operation_data() + * to retrieve an operation context set by the start callback. + * It may call mbedtls_ssl_set_async_operation_data() to modify + * this context. + * + * Note that when this function returns a status other than + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS, it must free any + * resources associated with the operation. + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param output Buffer containing the output (signature or decrypted + * data) on success. + * \param output_len On success, number of bytes written to \p output. + * \param output_size Size of the \p output buffer in bytes. + * + * \return 0 if output of the operation is available in the + * \p output buffer. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * is still in progress. Subsequent requests for progress + * on the SSL connection will call the resume callback + * again. + * \return Any other error means that the operation is aborted. + * The SSL handshake is aborted. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and <b>must not</b> + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_resume_t( mbedtls_ssl_context *ssl, + unsigned char *output, + size_t *output_len, + size_t output_size ); + +/** + * \brief Callback type: cancel external operation. + * + * This callback is called if an SSL connection is closed + * while an asynchronous operation is in progress. Note that + * this callback is not called if the + * ::mbedtls_ssl_async_resume_t callback has run and has + * returned a value other than + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS, since in that case + * the asynchronous operation has already completed. + * + * This function may call mbedtls_ssl_get_async_operation_data() + * to retrieve an operation context set by the start callback. + * + * \param ssl The SSL connection instance. It should not be + * modified. + */ +typedef void mbedtls_ssl_async_cancel_t( mbedtls_ssl_context *ssl ); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + /* * This structure is used for storing current session data. */ @@ -669,6 +889,16 @@ struct mbedtls_ssl_config mbedtls_x509_crl *ca_crl; /*!< trusted CAs CRLs */ #endif /* MBEDTLS_X509_CRT_PARSE_C */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +#if defined(MBEDTLS_X509_CRT_PARSE_C) + mbedtls_ssl_async_sign_t *f_async_sign_start; /*!< start asynchronous signature operation */ + mbedtls_ssl_async_decrypt_t *f_async_decrypt_start; /*!< start asynchronous decryption operation */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + mbedtls_ssl_async_resume_t *f_async_resume; /*!< resume asynchronous operation */ + mbedtls_ssl_async_cancel_t *f_async_cancel; /*!< cancel asynchronous operation */ + void *p_async_config_data; /*!< Configuration data set by mbedtls_ssl_conf_async_private_cb(). */ +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + #if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) const int *sig_hashes; /*!< allowed signature hashes */ #endif @@ -1307,6 +1537,85 @@ void mbedtls_ssl_conf_export_keys_cb( mbedtls_ssl_config *conf, void *p_export_keys ); #endif /* MBEDTLS_SSL_EXPORT_KEYS */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +/** + * \brief Configure asynchronous private key operation callbacks. + * + * \param conf SSL configuration context + * \param f_async_sign Callback to start a signature operation. See + * the description of ::mbedtls_ssl_async_sign_t + * for more information. This may be \c NULL if the + * external processor does not support any signature + * operation; in this case the private key object + * associated with the certificate will be used. + * \param f_async_decrypt Callback to start a decryption operation. See + * the description of ::mbedtls_ssl_async_decrypt_t + * for more information. This may be \c NULL if the + * external processor does not support any decryption + * operation; in this case the private key object + * associated with the certificate will be used. + * \param f_async_resume Callback to resume an asynchronous operation. See + * the description of ::mbedtls_ssl_async_resume_t + * for more information. This may not be \c NULL unless + * \p f_async_sign and \p f_async_decrypt are both + * \c NULL. + * \param f_async_cancel Callback to cancel an asynchronous operation. See + * the description of ::mbedtls_ssl_async_cancel_t + * for more information. This may be \c NULL if + * no cleanup is needed. + * \param config_data A pointer to configuration data which can be + * retrieved with + * mbedtls_ssl_conf_get_async_config_data(). The + * library stores this value without dereferencing it. + */ +void mbedtls_ssl_conf_async_private_cb( mbedtls_ssl_config *conf, + mbedtls_ssl_async_sign_t *f_async_sign, + mbedtls_ssl_async_decrypt_t *f_async_decrypt, + mbedtls_ssl_async_resume_t *f_async_resume, + mbedtls_ssl_async_cancel_t *f_async_cancel, + void *config_data ); + +/** + * \brief Retrieve the configuration data set by + * mbedtls_ssl_conf_async_private_cb(). + * + * \param conf SSL configuration context + * \return The configuration data set by + * mbedtls_ssl_conf_async_private_cb(). + */ +void *mbedtls_ssl_conf_get_async_config_data( const mbedtls_ssl_config *conf ); + +/** + * \brief Retrieve the asynchronous operation user context. + * + * \note This function may only be called while a handshake + * is in progress. + * + * \param ssl The SSL context to access. + * + * \return The asynchronous operation user context that was last + * set during the current handshake. If + * mbedtls_ssl_set_async_operation_data() has not yet been + * called during the current handshake, this function returns + * \c NULL. + */ +void *mbedtls_ssl_get_async_operation_data( const mbedtls_ssl_context *ssl ); + +/** + * \brief Retrieve the asynchronous operation user context. + * + * \note This function may only be called while a handshake + * is in progress. + * + * \param ssl The SSL context to access. + * \param ctx The new value of the asynchronous operation user context. + * Call mbedtls_ssl_get_async_operation_data() later during the + * same handshake to retrieve this value. + */ +void mbedtls_ssl_set_async_operation_data( mbedtls_ssl_context *ssl, + void *ctx ); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + /** * \brief Callback type: generate a cookie * @@ -2117,7 +2426,8 @@ void mbedtls_ssl_conf_cert_req_ca_list( mbedtls_ssl_config *conf, #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) /** * \brief Set the maximum fragment length to emit and/or negotiate - * (Default: MBEDTLS_SSL_MAX_CONTENT_LEN, usually 2^14 bytes) + * (Default: the smaller of MBEDTLS_SSL_IN_CONTENT_LEN and + * MBEDTLS_SSL_OUT_CONTENT_LEN, usually 2^14 bytes) * (Server: set maximum fragment length to emit, * usually negotiated by the client during handshake * (Client: set maximum fragment length to emit *and* @@ -2436,7 +2746,6 @@ const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert( const mbedtls_ssl_context *ss * \brief Save session in order to resume it later (client-side only) * Session data is copied to presented session structure. * - * \warning Currently, peer certificate is lost in the operation. * * \param ssl SSL context * \param session session context @@ -2444,7 +2753,18 @@ const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert( const mbedtls_ssl_context *ss * \return 0 if successful, * MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed, * MBEDTLS_ERR_SSL_BAD_INPUT_DATA if used server-side or - * arguments are otherwise invalid + * arguments are otherwise invalid. + * + * \note Only the server certificate is copied, and not the full chain, + * so you should not attempt to validate the certificate again + * by calling \c mbedtls_x509_crt_verify() on it. + * Instead, you should use the results from the verification + * in the original handshake by calling \c mbedtls_ssl_get_verify_result() + * after loading the session again into a new SSL context + * using \c mbedtls_ssl_set_session(). + * + * \note Once the session object is not needed anymore, you should + * free it by calling \c mbedtls_ssl_session_free(). * * \sa mbedtls_ssl_set_session() */ @@ -2606,17 +2926,19 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ) * or MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ, * or another negative error code. * - * \note If this function returns something other than a positive value - * or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using - * the SSL context for reading or writing, and either free it or - * call \c mbedtls_ssl_session_reset() on it before re-using it - * for a new connection; the current connection must be closed. + * \note If this function returns something other than 0, a positive + * value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop + * using the SSL context for reading or writing, and either + * free it or call \c mbedtls_ssl_session_reset() on it before + * re-using it for a new connection; the current connection + * must be closed. * * \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, * it must be called later with the *same* arguments, - * until it returns a positive value. When the function returns - * MBEDTLS_ERR_SSL_WANT_WRITE there may be some partial - * data in the output buffer, however this is not yet sent. + * until it returns a value greater that or equal to 0. When + * the function returns MBEDTLS_ERR_SSL_WANT_WRITE there may be + * some partial data in the output buffer, however this is not + * yet sent. * * \note If the requested length is greater than the maximum * fragment length (either the built-in limit or the one set @@ -2625,6 +2947,9 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ) * - with DTLS, MBEDTLS_ERR_SSL_BAD_INPUT_DATA is returned. * \c mbedtls_ssl_get_max_frag_len() may be used to query the * active maximum fragment length. + * + * \note Attempting to write 0 bytes will result in an empty TLS + * application record being sent. */ int mbedtls_ssl_write( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len ); @@ -2717,6 +3042,9 @@ void mbedtls_ssl_session_init( mbedtls_ssl_session *session ); * \brief Free referenced items in an SSL session including the * peer certificate and clear memory * + * \note A session object can be freed even if the SSL context + * that was used to retrieve the session is still in use. + * * \param session SSL session */ void mbedtls_ssl_session_free( mbedtls_ssl_session *session ); diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h b/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h index 7d5eba0916..cda8b4835b 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h @@ -271,6 +271,15 @@ extern "C" { #define MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 0xC0FF /**< experimental */ +/* RFC 7905 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAB /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAC /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE /**< TLS 1.2 */ + /* Reminder: update mbedtls_ssl_premaster_secret when adding a new key exchange. * Reminder: update MBEDTLS_KEY_EXCHANGE__xxx below */ diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h index 60b431a0f4..d214703d77 100644 --- a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h +++ b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h @@ -143,32 +143,73 @@ #define MBEDTLS_SSL_PADDING_ADD 0 #endif -#define MBEDTLS_SSL_PAYLOAD_LEN ( MBEDTLS_SSL_MAX_CONTENT_LEN \ - + MBEDTLS_SSL_COMPRESSION_ADD \ - + MBEDTLS_MAX_IV_LENGTH \ - + MBEDTLS_SSL_MAC_ADD \ - + MBEDTLS_SSL_PADDING_ADD \ - ) +#define MBEDTLS_SSL_PAYLOAD_OVERHEAD ( MBEDTLS_SSL_COMPRESSION_ADD + \ + MBEDTLS_MAX_IV_LENGTH + \ + MBEDTLS_SSL_MAC_ADD + \ + MBEDTLS_SSL_PADDING_ADD \ + ) + +#define MBEDTLS_SSL_IN_PAYLOAD_LEN ( MBEDTLS_SSL_PAYLOAD_OVERHEAD + \ + ( MBEDTLS_SSL_IN_CONTENT_LEN ) ) + +#define MBEDTLS_SSL_OUT_PAYLOAD_LEN ( MBEDTLS_SSL_PAYLOAD_OVERHEAD + \ + ( MBEDTLS_SSL_OUT_CONTENT_LEN ) ) + +/* Maximum length we can advertise as our max content length for + RFC 6066 max_fragment_length extension negotiation purposes + (the lesser of both sizes, if they are unequal.) + */ +#define MBEDTLS_TLS_EXT_ADV_CONTENT_LEN ( \ + (MBEDTLS_SSL_IN_CONTENT_LEN > MBEDTLS_SSL_OUT_CONTENT_LEN) \ + ? ( MBEDTLS_SSL_OUT_CONTENT_LEN ) \ + : ( MBEDTLS_SSL_IN_CONTENT_LEN ) \ + ) /* * Check that we obey the standard's message size bounds */ #if MBEDTLS_SSL_MAX_CONTENT_LEN > 16384 -#error Bad configuration - record content too large. +#error "Bad configuration - record content too large." +#endif + +#if MBEDTLS_SSL_IN_CONTENT_LEN > MBEDTLS_SSL_MAX_CONTENT_LEN +#error "Bad configuration - incoming record content should not be larger than MBEDTLS_SSL_MAX_CONTENT_LEN." +#endif + +#if MBEDTLS_SSL_OUT_CONTENT_LEN > MBEDTLS_SSL_MAX_CONTENT_LEN +#error "Bad configuration - outgoing record content should not be larger than MBEDTLS_SSL_MAX_CONTENT_LEN." +#endif + +#if MBEDTLS_SSL_IN_PAYLOAD_LEN > MBEDTLS_SSL_MAX_CONTENT_LEN + 2048 +#error "Bad configuration - incoming protected record payload too large." #endif -#if MBEDTLS_SSL_PAYLOAD_LEN > 16384 + 2048 -#error Bad configuration - protected record payload too large. +#if MBEDTLS_SSL_OUT_PAYLOAD_LEN > MBEDTLS_SSL_MAX_CONTENT_LEN + 2048 +#error "Bad configuration - outgoing protected record payload too large." #endif +/* Calculate buffer sizes */ + /* Note: Even though the TLS record header is only 5 bytes long, we're internally using 8 bytes to store the implicit sequence number. */ #define MBEDTLS_SSL_HEADER_LEN 13 -#define MBEDTLS_SSL_BUFFER_LEN \ - ( ( MBEDTLS_SSL_HEADER_LEN ) + ( MBEDTLS_SSL_PAYLOAD_LEN ) ) +#define MBEDTLS_SSL_IN_BUFFER_LEN \ + ( ( MBEDTLS_SSL_HEADER_LEN ) + ( MBEDTLS_SSL_IN_PAYLOAD_LEN ) ) + +#define MBEDTLS_SSL_OUT_BUFFER_LEN \ + ( ( MBEDTLS_SSL_HEADER_LEN ) + ( MBEDTLS_SSL_OUT_PAYLOAD_LEN ) ) + +#ifdef MBEDTLS_ZLIB_SUPPORT +/* Compression buffer holds both IN and OUT buffers, so should be size of the larger */ +#define MBEDTLS_SSL_COMPRESS_BUFFER_LEN ( \ + ( MBEDTLS_SSL_IN_BUFFER_LEN > MBEDTLS_SSL_OUT_BUFFER_LEN ) \ + ? MBEDTLS_SSL_IN_BUFFER_LEN \ + : MBEDTLS_SSL_OUT_BUFFER_LEN \ + ) +#endif /* * TLS extension flags (for extensions with outgoing ServerHello content @@ -243,6 +284,7 @@ struct mbedtls_ssl_handshake_params mbedtls_x509_crl *sni_ca_crl; /*!< trusted CAs CRLs from SNI */ #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ #endif /* MBEDTLS_X509_CRT_PARSE_C */ + #if defined(MBEDTLS_SSL_PROTO_DTLS) unsigned int out_msg_seq; /*!< Outgoing handshake sequence number */ unsigned int in_msg_seq; /*!< Incoming handshake sequence number */ @@ -307,6 +349,19 @@ struct mbedtls_ssl_handshake_params #if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) int extended_ms; /*!< use Extended Master Secret? */ #endif + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + unsigned int async_in_progress : 1; /*!< an asynchronous operation is in progress */ +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /** Asynchronous operation context. This field is meant for use by the + * asynchronous operation callbacks (mbedtls_ssl_config::f_async_sign_start, + * mbedtls_ssl_config::f_async_decrypt_start, + * mbedtls_ssl_config::f_async_resume, mbedtls_ssl_config::f_async_cancel). + * The library does not use it internally. */ + void *user_async_ctx; +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ }; /* @@ -410,9 +465,9 @@ void mbedtls_ssl_transform_free( mbedtls_ssl_transform *transform ); * \brief Free referenced items in an SSL handshake context and clear * memory * - * \param handshake SSL handshake context + * \param ssl SSL context */ -void mbedtls_ssl_handshake_free( mbedtls_ssl_handshake_params *handshake ); +void mbedtls_ssl_handshake_free( mbedtls_ssl_context *ssl ); int mbedtls_ssl_handshake_client_step( mbedtls_ssl_context *ssl ); int mbedtls_ssl_handshake_server_step( mbedtls_ssl_context *ssl ); @@ -630,7 +685,13 @@ static inline int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t volatile unsigned char diff = 0; for( i = 0; i < n; i++ ) - diff |= A[i] ^ B[i]; + { + /* Read volatile data in order before computing diff. + * This avoids IAR compiler warning: + * 'the order of volatile accesses is undefined ..' */ + unsigned char x = A[i], y = B[i]; + diff |= x ^ y; + } return( diff ); } @@ -646,9 +707,9 @@ int mbedtls_ssl_get_key_exchange_md_ssl_tls( mbedtls_ssl_context *ssl, #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_2) int mbedtls_ssl_get_key_exchange_md_tls1_2( mbedtls_ssl_context *ssl, - unsigned char *output, - unsigned char *data, size_t data_len, - mbedtls_md_type_t md_alg ); + unsigned char *hash, size_t *hashlen, + unsigned char *data, size_t data_len, + mbedtls_md_type_t md_alg ); #endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \ MBEDTLS_SSL_PROTO_TLS1_2 */ diff --git a/thirdparty/mbedtls/include/mbedtls/threading.h b/thirdparty/mbedtls/include/mbedtls/threading.h index aeea5d0e1a..c25daa5cdf 100644 --- a/thirdparty/mbedtls/include/mbedtls/threading.h +++ b/thirdparty/mbedtls/include/mbedtls/threading.h @@ -99,9 +99,6 @@ extern int (*mbedtls_mutex_unlock)( mbedtls_threading_mutex_t *mutex ); #if defined(MBEDTLS_FS_IO) extern mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex; #endif -#if defined(MBEDTLS_HAVE_TIME_DATE) -extern mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex; -#endif #endif /* MBEDTLS_THREADING_C */ #ifdef __cplusplus diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h index 83e3c1726b..eaf25d908c 100644 --- a/thirdparty/mbedtls/include/mbedtls/version.h +++ b/thirdparty/mbedtls/include/mbedtls/version.h @@ -39,7 +39,7 @@ * Major, Minor, Patchlevel */ #define MBEDTLS_VERSION_MAJOR 2 -#define MBEDTLS_VERSION_MINOR 10 +#define MBEDTLS_VERSION_MINOR 12 #define MBEDTLS_VERSION_PATCH 0 /** @@ -47,9 +47,9 @@ * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x020A0000 -#define MBEDTLS_VERSION_STRING "2.10.0" -#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.10.0" +#define MBEDTLS_VERSION_NUMBER 0x020C0000 +#define MBEDTLS_VERSION_STRING "2.12.0" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.12.0" #if defined(MBEDTLS_VERSION_C) diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c index fea9b5383d..5c939bba47 100644 --- a/thirdparty/mbedtls/library/aes.c +++ b/thirdparty/mbedtls/library/aes.c @@ -521,6 +521,20 @@ void mbedtls_aes_free( mbedtls_aes_context *ctx ) mbedtls_platform_zeroize( ctx, sizeof( mbedtls_aes_context ) ); } +#if defined(MBEDTLS_CIPHER_MODE_XTS) +void mbedtls_aes_xts_init( mbedtls_aes_xts_context *ctx ) +{ + mbedtls_aes_init( &ctx->crypt ); + mbedtls_aes_init( &ctx->tweak ); +} + +void mbedtls_aes_xts_free( mbedtls_aes_xts_context *ctx ) +{ + mbedtls_aes_free( &ctx->crypt ); + mbedtls_aes_free( &ctx->tweak ); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + /* * AES key schedule (encryption) */ @@ -702,6 +716,78 @@ exit: return( ret ); } + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int mbedtls_aes_xts_decode_keys( const unsigned char *key, + unsigned int keybits, + const unsigned char **key1, + unsigned int *key1bits, + const unsigned char **key2, + unsigned int *key2bits ) +{ + const unsigned int half_keybits = keybits / 2; + const unsigned int half_keybytes = half_keybits / 8; + + switch( keybits ) + { + case 256: break; + case 512: break; + default : return( MBEDTLS_ERR_AES_INVALID_KEY_LENGTH ); + } + + *key1bits = half_keybits; + *key2bits = half_keybits; + *key1 = &key[0]; + *key2 = &key[half_keybytes]; + + return 0; +} + +int mbedtls_aes_xts_setkey_enc( mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int ret; + const unsigned char *key1, *key2; + unsigned int key1bits, key2bits; + + ret = mbedtls_aes_xts_decode_keys( key, keybits, &key1, &key1bits, + &key2, &key2bits ); + if( ret != 0 ) + return( ret ); + + /* Set the tweak key. Always set tweak key for the encryption mode. */ + ret = mbedtls_aes_setkey_enc( &ctx->tweak, key2, key2bits ); + if( ret != 0 ) + return( ret ); + + /* Set crypt key for encryption. */ + return mbedtls_aes_setkey_enc( &ctx->crypt, key1, key1bits ); +} + +int mbedtls_aes_xts_setkey_dec( mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int ret; + const unsigned char *key1, *key2; + unsigned int key1bits, key2bits; + + ret = mbedtls_aes_xts_decode_keys( key, keybits, &key1, &key1bits, + &key2, &key2bits ); + if( ret != 0 ) + return( ret ); + + /* Set the tweak key. Always set tweak key for encryption. */ + ret = mbedtls_aes_setkey_enc( &ctx->tweak, key2, key2bits ); + if( ret != 0 ) + return( ret ); + + /* Set crypt key for decryption. */ + return mbedtls_aes_setkey_dec( &ctx->crypt, key1, key1bits ); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #endif /* !MBEDTLS_AES_SETKEY_DEC_ALT */ #define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ @@ -983,6 +1069,165 @@ int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx, } #endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) + +/* Endianess with 64 bits values */ +#ifndef GET_UINT64_LE +#define GET_UINT64_LE(n,b,i) \ +{ \ + (n) = ( (uint64_t) (b)[(i) + 7] << 56 ) \ + | ( (uint64_t) (b)[(i) + 6] << 48 ) \ + | ( (uint64_t) (b)[(i) + 5] << 40 ) \ + | ( (uint64_t) (b)[(i) + 4] << 32 ) \ + | ( (uint64_t) (b)[(i) + 3] << 24 ) \ + | ( (uint64_t) (b)[(i) + 2] << 16 ) \ + | ( (uint64_t) (b)[(i) + 1] << 8 ) \ + | ( (uint64_t) (b)[(i) ] ); \ +} +#endif + +#ifndef PUT_UINT64_LE +#define PUT_UINT64_LE(n,b,i) \ +{ \ + (b)[(i) + 7] = (unsigned char) ( (n) >> 56 ); \ + (b)[(i) + 6] = (unsigned char) ( (n) >> 48 ); \ + (b)[(i) + 5] = (unsigned char) ( (n) >> 40 ); \ + (b)[(i) + 4] = (unsigned char) ( (n) >> 32 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) ] = (unsigned char) ( (n) ); \ +} +#endif + +typedef unsigned char mbedtls_be128[16]; + +/* + * GF(2^128) multiplication function + * + * This function multiplies a field element by x in the polynomial field + * representation. It uses 64-bit word operations to gain speed but compensates + * for machine endianess and hence works correctly on both big and little + * endian machines. + */ +static void mbedtls_gf128mul_x_ble( unsigned char r[16], + const unsigned char x[16] ) +{ + uint64_t a, b, ra, rb; + + GET_UINT64_LE( a, x, 0 ); + GET_UINT64_LE( b, x, 8 ); + + ra = ( a << 1 ) ^ 0x0087 >> ( 8 - ( ( b >> 63 ) << 3 ) ); + rb = ( a >> 63 ) | ( b << 1 ); + + PUT_UINT64_LE( ra, r, 0 ); + PUT_UINT64_LE( rb, r, 8 ); +} + +/* + * AES-XTS buffer encryption/decryption + */ +int mbedtls_aes_crypt_xts( mbedtls_aes_xts_context *ctx, + int mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t blocks = length / 16; + size_t leftover = length % 16; + unsigned char tweak[16]; + unsigned char prev_tweak[16]; + unsigned char tmp[16]; + + /* Sectors must be at least 16 bytes. */ + if( length < 16 ) + return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; + + /* NIST SP 80-38E disallows data units larger than 2**20 blocks. */ + if( length > ( 1 << 20 ) * 16 ) + return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; + + /* Compute the tweak. */ + ret = mbedtls_aes_crypt_ecb( &ctx->tweak, MBEDTLS_AES_ENCRYPT, + data_unit, tweak ); + if( ret != 0 ) + return( ret ); + + while( blocks-- ) + { + size_t i; + + if( leftover && ( mode == MBEDTLS_AES_DECRYPT ) && blocks == 0 ) + { + /* We are on the last block in a decrypt operation that has + * leftover bytes, so we need to use the next tweak for this block, + * and this tweak for the lefover bytes. Save the current tweak for + * the leftovers and then update the current tweak for use on this, + * the last full block. */ + memcpy( prev_tweak, tweak, sizeof( tweak ) ); + mbedtls_gf128mul_x_ble( tweak, tweak ); + } + + for( i = 0; i < 16; i++ ) + tmp[i] = input[i] ^ tweak[i]; + + ret = mbedtls_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp ); + if( ret != 0 ) + return( ret ); + + for( i = 0; i < 16; i++ ) + output[i] = tmp[i] ^ tweak[i]; + + /* Update the tweak for the next block. */ + mbedtls_gf128mul_x_ble( tweak, tweak ); + + output += 16; + input += 16; + } + + if( leftover ) + { + /* If we are on the leftover bytes in a decrypt operation, we need to + * use the previous tweak for these bytes (as saved in prev_tweak). */ + unsigned char *t = mode == MBEDTLS_AES_DECRYPT ? prev_tweak : tweak; + + /* We are now on the final part of the data unit, which doesn't divide + * evenly by 16. It's time for ciphertext stealing. */ + size_t i; + unsigned char *prev_output = output - 16; + + /* Copy ciphertext bytes from the previous block to our output for each + * byte of cyphertext we won't steal. At the same time, copy the + * remainder of the input for this final round (since the loop bounds + * are the same). */ + for( i = 0; i < leftover; i++ ) + { + output[i] = prev_output[i]; + tmp[i] = input[i] ^ t[i]; + } + + /* Copy ciphertext bytes from the previous block for input in this + * round. */ + for( ; i < 16; i++ ) + tmp[i] = prev_output[i] ^ t[i]; + + ret = mbedtls_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp ); + if( ret != 0 ) + return ret; + + /* Write the result back to the previous block, overriding the previous + * output we copied. */ + for( i = 0; i < 16; i++ ) + prev_output[i] = tmp[i] ^ t[i]; + } + + return( 0 ); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #if defined(MBEDTLS_CIPHER_MODE_CFB) /* * AES-CFB128 buffer encryption/decryption @@ -1061,7 +1306,41 @@ int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx, return( 0 ); } -#endif /*MBEDTLS_CIPHER_MODE_CFB */ +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/* + * AES-OFB (Output Feedback Mode) buffer encryption/decryption + */ +int mbedtls_aes_crypt_ofb( mbedtls_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int ret = 0; + size_t n = *iv_off; + + while( length-- ) + { + if( n == 0 ) + { + ret = mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv ); + if( ret != 0 ) + goto exit; + } + *output++ = *input++ ^ iv[n]; + + n = ( n + 1 ) & 0x0F; + } + + *iv_off = n; + +exit: + return( ret ); +} +#endif /* MBEDTLS_CIPHER_MODE_OFB */ #if defined(MBEDTLS_CIPHER_MODE_CTR) /* @@ -1218,6 +1497,72 @@ static const unsigned char aes_test_cfb128_ct[3][64] = }; #endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/* + * AES-OFB test vectors from: + * + * https://csrc.nist.gov/publications/detail/sp/800-38a/final + */ +static const unsigned char aes_test_ofb_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_ofb_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_ofb_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_ofb_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, + 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25, + 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, + 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc, + 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, + 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0xfc, 0xc2, 0x8b, 0x8d, 0x4c, 0x63, 0x83, 0x7c, + 0x09, 0xe8, 0x17, 0x00, 0xc1, 0x10, 0x04, 0x01, + 0x8d, 0x9a, 0x9a, 0xea, 0xc0, 0xf6, 0x59, 0x6f, + 0x55, 0x9c, 0x6d, 0x4d, 0xaf, 0x59, 0xa5, 0xf2, + 0x6d, 0x9f, 0x20, 0x08, 0x57, 0xca, 0x6c, 0x3e, + 0x9c, 0xac, 0x52, 0x4b, 0xd9, 0xac, 0xc9, 0x2a }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x4f, 0xeb, 0xdc, 0x67, 0x40, 0xd2, 0x0b, 0x3a, + 0xc8, 0x8f, 0x6a, 0xd8, 0x2a, 0x4f, 0xb0, 0x8d, + 0x71, 0xab, 0x47, 0xa0, 0x86, 0xe8, 0x6e, 0xed, + 0xf3, 0x9d, 0x1c, 0x5b, 0xba, 0x97, 0xc4, 0x08, + 0x01, 0x26, 0x14, 0x1d, 0x67, 0xf3, 0x7b, 0xe8, + 0x53, 0x8f, 0x5a, 0x8b, 0xe7, 0x40, 0xe4, 0x84 } +}; +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) /* * AES-CTR test vectors from: @@ -1281,6 +1626,74 @@ static const int aes_test_ctr_len[3] = { 16, 32, 36 }; #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/* + * AES-XTS test vectors from: + * + * IEEE P1619/D16 Annex B + * https://web.archive.org/web/20150629024421/http://grouper.ieee.org/groups/1619/email/pdf00086.pdf + * (Archived from original at http://grouper.ieee.org/groups/1619/email/pdf00086.pdf) + */ +static const unsigned char aes_test_xts_key[][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, +}; + +static const unsigned char aes_test_xts_pt32[][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, +}; + +static const unsigned char aes_test_xts_ct32[][32] = +{ + { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, + 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, + 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, + 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, + { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, + 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, + 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, + 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, + { 0xaf, 0x85, 0x33, 0x6b, 0x59, 0x7a, 0xfc, 0x1a, + 0x90, 0x0b, 0x2e, 0xb2, 0x1e, 0xc9, 0x49, 0xd2, + 0x92, 0xdf, 0x4c, 0x04, 0x7e, 0x0b, 0x21, 0x53, + 0x21, 0x86, 0xa5, 0x97, 0x1a, 0x22, 0x7a, 0x89 }, +}; + +static const unsigned char aes_test_xts_data_unit[][16] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; + +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + /* * Checkup routine */ @@ -1297,11 +1710,14 @@ int mbedtls_aes_self_test( int verbose ) #if defined(MBEDTLS_CIPHER_MODE_CBC) unsigned char prv[16]; #endif -#if defined(MBEDTLS_CIPHER_MODE_CTR) || defined(MBEDTLS_CIPHER_MODE_CFB) +#if defined(MBEDTLS_CIPHER_MODE_CTR) || defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_OFB) size_t offset; #endif -#if defined(MBEDTLS_CIPHER_MODE_CTR) +#if defined(MBEDTLS_CIPHER_MODE_CTR) || defined(MBEDTLS_CIPHER_MODE_XTS) int len; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) unsigned char nonce_counter[16]; unsigned char stream_block[16]; #endif @@ -1509,6 +1925,69 @@ int mbedtls_aes_self_test( int verbose ) mbedtls_printf( "\n" ); #endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) + /* + * OFB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + keybits = 128 + u * 64; + mode = i & 1; + + if( verbose != 0 ) + mbedtls_printf( " AES-OFB-%3d (%s): ", keybits, + ( mode == MBEDTLS_AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_ofb_iv, 16 ); + memcpy( key, aes_test_ofb_key[u], keybits / 8 ); + + offset = 0; + ret = mbedtls_aes_setkey_enc( &ctx, key, keybits ); + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if( ret == MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE && keybits == 192 ) + { + mbedtls_printf( "skipped\n" ); + continue; + } + else if( ret != 0 ) + { + goto exit; + } + + if( mode == MBEDTLS_AES_DECRYPT ) + { + memcpy( buf, aes_test_ofb_ct[u], 64 ); + aes_tests = aes_test_ofb_pt; + } + else + { + memcpy( buf, aes_test_ofb_pt, 64 ); + aes_tests = aes_test_ofb_ct[u]; + } + + ret = mbedtls_aes_crypt_ofb( &ctx, 64, &offset, iv, buf, buf ); + if( ret != 0 ) + goto exit; + + if( memcmp( buf, aes_tests, 64 ) != 0 ) + { + ret = 1; + goto exit; + } + + if( verbose != 0 ) + mbedtls_printf( "passed\n" ); + } + + if( verbose != 0 ) + mbedtls_printf( "\n" ); +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) /* * CTR mode @@ -1561,6 +2040,73 @@ int mbedtls_aes_self_test( int verbose ) mbedtls_printf( "\n" ); #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) + { + static const int num_tests = + sizeof(aes_test_xts_key) / sizeof(*aes_test_xts_key); + mbedtls_aes_xts_context ctx_xts; + + /* + * XTS mode + */ + mbedtls_aes_xts_init( &ctx_xts ); + + for( i = 0; i < num_tests << 1; i++ ) + { + const unsigned char *data_unit; + u = i >> 1; + mode = i & 1; + + if( verbose != 0 ) + mbedtls_printf( " AES-XTS-128 (%s): ", + ( mode == MBEDTLS_AES_DECRYPT ) ? "dec" : "enc" ); + + memset( key, 0, sizeof( key ) ); + memcpy( key, aes_test_xts_key[u], 32 ); + data_unit = aes_test_xts_data_unit[u]; + + len = sizeof( *aes_test_xts_ct32 ); + + if( mode == MBEDTLS_AES_DECRYPT ) + { + ret = mbedtls_aes_xts_setkey_dec( &ctx_xts, key, 256 ); + if( ret != 0) + goto exit; + memcpy( buf, aes_test_xts_ct32[u], len ); + aes_tests = aes_test_xts_pt32[u]; + } + else + { + ret = mbedtls_aes_xts_setkey_enc( &ctx_xts, key, 256 ); + if( ret != 0) + goto exit; + memcpy( buf, aes_test_xts_pt32[u], len ); + aes_tests = aes_test_xts_ct32[u]; + } + + + ret = mbedtls_aes_crypt_xts( &ctx_xts, mode, len, data_unit, + buf, buf ); + if( ret != 0 ) + goto exit; + + if( memcmp( buf, aes_tests, len ) != 0 ) + { + ret = 1; + goto exit; + } + + if( verbose != 0 ) + mbedtls_printf( "passed\n" ); + } + + if( verbose != 0 ) + mbedtls_printf( "\n" ); + + mbedtls_aes_xts_free( &ctx_xts ); + } +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + ret = 0; exit: diff --git a/thirdparty/mbedtls/library/asn1write.c b/thirdparty/mbedtls/library/asn1write.c index c01c836550..72acdf3012 100644 --- a/thirdparty/mbedtls/library/asn1write.c +++ b/thirdparty/mbedtls/library/asn1write.c @@ -83,7 +83,9 @@ int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start, size_t len return( 4 ); } +#if SIZE_MAX > 0xFFFFFFFF if( len <= 0xFFFFFFFF ) +#endif { if( *p - start < 5 ) return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL ); @@ -96,7 +98,9 @@ int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start, size_t len return( 5 ); } +#if SIZE_MAX > 0xFFFFFFFF return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); +#endif } int mbedtls_asn1_write_tag( unsigned char **p, unsigned char *start, unsigned char tag ) diff --git a/thirdparty/mbedtls/library/ccm.c b/thirdparty/mbedtls/library/ccm.c index cf6520935e..804eaf80f1 100644 --- a/thirdparty/mbedtls/library/ccm.c +++ b/thirdparty/mbedtls/library/ccm.c @@ -152,8 +152,10 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, * Check length requirements: SP800-38C A.1 * Additional requirement: a < 2^16 - 2^8 to simplify the code. * 'length' checked later (when writing it to the first block) + * + * Also, loosen the requirements to enable support for CCM* (IEEE 802.15.4). */ - if( tag_len < 4 || tag_len > 16 || tag_len % 2 != 0 ) + if( tag_len == 2 || tag_len > 16 || tag_len % 2 != 0 ) return( MBEDTLS_ERR_CCM_BAD_INPUT ); /* Also implies q is within bounds */ @@ -302,7 +304,7 @@ static int ccm_auth_crypt( mbedtls_ccm_context *ctx, int mode, size_t length, /* * Authenticated encryption */ -int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, +int mbedtls_ccm_star_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, const unsigned char *iv, size_t iv_len, const unsigned char *add, size_t add_len, const unsigned char *input, unsigned char *output, @@ -312,10 +314,23 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, add, add_len, input, output, tag, tag_len ) ); } +int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ) +{ + if( tag_len == 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + return( mbedtls_ccm_star_encrypt_and_tag( ctx, length, iv, iv_len, add, + add_len, input, output, tag, tag_len ) ); +} + /* * Authenticated decryption */ -int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, +int mbedtls_ccm_star_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, const unsigned char *iv, size_t iv_len, const unsigned char *add, size_t add_len, const unsigned char *input, unsigned char *output, @@ -346,6 +361,18 @@ int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, return( 0 ); } +int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ) +{ + if( tag_len == 0 ) + return( MBEDTLS_ERR_CCM_BAD_INPUT ); + + return( mbedtls_ccm_star_auth_decrypt( ctx, length, iv, iv_len, add, + add_len, input, output, tag, tag_len ) ); +} #endif /* !MBEDTLS_CCM_ALT */ #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) diff --git a/thirdparty/mbedtls/library/chacha20.c b/thirdparty/mbedtls/library/chacha20.c new file mode 100644 index 0000000000..d14a51e044 --- /dev/null +++ b/thirdparty/mbedtls/library/chacha20.c @@ -0,0 +1,570 @@ +/** + * \file chacha20.c + * + * \brief ChaCha20 cipher. + * + * \author Daniel King <damaki.gh@gmail.com> + * + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_CHACHA20_C) + +#include "mbedtls/chacha20.h" +#include "mbedtls/platform_util.h" + +#include <stddef.h> +#include <string.h> + +#if defined(MBEDTLS_SELF_TEST) +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include <stdio.h> +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_C */ +#endif /* MBEDTLS_SELF_TEST */ + +#if !defined(MBEDTLS_CHACHA20_ALT) + +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +#define BYTES_TO_U32_LE( data, offset ) \ + ( (uint32_t) data[offset] \ + | (uint32_t) ( (uint32_t) data[( offset ) + 1] << 8 ) \ + | (uint32_t) ( (uint32_t) data[( offset ) + 2] << 16 ) \ + | (uint32_t) ( (uint32_t) data[( offset ) + 3] << 24 ) \ + ) + +#define ROTL32( value, amount ) \ + ( (uint32_t) ( value << amount ) | ( value >> ( 32 - amount ) ) ) + +#define CHACHA20_CTR_INDEX ( 12U ) + +#define CHACHA20_BLOCK_SIZE_BYTES ( 4U * 16U ) + +/** + * \brief ChaCha20 quarter round operation. + * + * The quarter round is defined as follows (from RFC 7539): + * 1. a += b; d ^= a; d <<<= 16; + * 2. c += d; b ^= c; b <<<= 12; + * 3. a += b; d ^= a; d <<<= 8; + * 4. c += d; b ^= c; b <<<= 7; + * + * \param state ChaCha20 state to modify. + * \param a The index of 'a' in the state. + * \param b The index of 'b' in the state. + * \param c The index of 'c' in the state. + * \param d The index of 'd' in the state. + */ +static inline void chacha20_quarter_round( uint32_t state[16], + size_t a, + size_t b, + size_t c, + size_t d ) +{ + /* a += b; d ^= a; d <<<= 16; */ + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTL32( state[d], 16 ); + + /* c += d; b ^= c; b <<<= 12 */ + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTL32( state[b], 12 ); + + /* a += b; d ^= a; d <<<= 8; */ + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTL32( state[d], 8 ); + + /* c += d; b ^= c; b <<<= 7; */ + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTL32( state[b], 7 ); +} + +/** + * \brief Perform the ChaCha20 inner block operation. + * + * This function performs two rounds: the column round and the + * diagonal round. + * + * \param state The ChaCha20 state to update. + */ +static void chacha20_inner_block( uint32_t state[16] ) +{ + chacha20_quarter_round( state, 0, 4, 8, 12 ); + chacha20_quarter_round( state, 1, 5, 9, 13 ); + chacha20_quarter_round( state, 2, 6, 10, 14 ); + chacha20_quarter_round( state, 3, 7, 11, 15 ); + + chacha20_quarter_round( state, 0, 5, 10, 15 ); + chacha20_quarter_round( state, 1, 6, 11, 12 ); + chacha20_quarter_round( state, 2, 7, 8, 13 ); + chacha20_quarter_round( state, 3, 4, 9, 14 ); +} + +/** + * \brief Generates a keystream block. + * + * \param initial_state The initial ChaCha20 state (key, nonce, counter). + * \param keystream Generated keystream bytes are written to this buffer. + */ +static void chacha20_block( const uint32_t initial_state[16], + unsigned char keystream[64] ) +{ + uint32_t working_state[16]; + size_t i; + + memcpy( working_state, + initial_state, + CHACHA20_BLOCK_SIZE_BYTES ); + + for( i = 0U; i < 10U; i++ ) + chacha20_inner_block( working_state ); + + working_state[ 0] += initial_state[ 0]; + working_state[ 1] += initial_state[ 1]; + working_state[ 2] += initial_state[ 2]; + working_state[ 3] += initial_state[ 3]; + working_state[ 4] += initial_state[ 4]; + working_state[ 5] += initial_state[ 5]; + working_state[ 6] += initial_state[ 6]; + working_state[ 7] += initial_state[ 7]; + working_state[ 8] += initial_state[ 8]; + working_state[ 9] += initial_state[ 9]; + working_state[10] += initial_state[10]; + working_state[11] += initial_state[11]; + working_state[12] += initial_state[12]; + working_state[13] += initial_state[13]; + working_state[14] += initial_state[14]; + working_state[15] += initial_state[15]; + + for( i = 0U; i < 16; i++ ) + { + size_t offset = i * 4U; + + keystream[offset ] = (unsigned char)( working_state[i] ); + keystream[offset + 1U] = (unsigned char)( working_state[i] >> 8 ); + keystream[offset + 2U] = (unsigned char)( working_state[i] >> 16 ); + keystream[offset + 3U] = (unsigned char)( working_state[i] >> 24 ); + } + + mbedtls_platform_zeroize( working_state, sizeof( working_state ) ); +} + +void mbedtls_chacha20_init( mbedtls_chacha20_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_platform_zeroize( ctx->state, sizeof( ctx->state ) ); + mbedtls_platform_zeroize( ctx->keystream8, sizeof( ctx->keystream8 ) ); + + /* Initially, there's no keystream bytes available */ + ctx->keystream_bytes_used = CHACHA20_BLOCK_SIZE_BYTES; + } +} + +void mbedtls_chacha20_free( mbedtls_chacha20_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_chacha20_context ) ); + } +} + +int mbedtls_chacha20_setkey( mbedtls_chacha20_context *ctx, + const unsigned char key[32] ) +{ + if( ( ctx == NULL ) || ( key == NULL ) ) + { + return( MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ); + } + + /* ChaCha20 constants - the string "expand 32-byte k" */ + ctx->state[0] = 0x61707865; + ctx->state[1] = 0x3320646e; + ctx->state[2] = 0x79622d32; + ctx->state[3] = 0x6b206574; + + /* Set key */ + ctx->state[4] = BYTES_TO_U32_LE( key, 0 ); + ctx->state[5] = BYTES_TO_U32_LE( key, 4 ); + ctx->state[6] = BYTES_TO_U32_LE( key, 8 ); + ctx->state[7] = BYTES_TO_U32_LE( key, 12 ); + ctx->state[8] = BYTES_TO_U32_LE( key, 16 ); + ctx->state[9] = BYTES_TO_U32_LE( key, 20 ); + ctx->state[10] = BYTES_TO_U32_LE( key, 24 ); + ctx->state[11] = BYTES_TO_U32_LE( key, 28 ); + + return( 0 ); +} + +int mbedtls_chacha20_starts( mbedtls_chacha20_context* ctx, + const unsigned char nonce[12], + uint32_t counter ) +{ + if( ( ctx == NULL ) || ( nonce == NULL ) ) + { + return( MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ); + } + + /* Counter */ + ctx->state[12] = counter; + + /* Nonce */ + ctx->state[13] = BYTES_TO_U32_LE( nonce, 0 ); + ctx->state[14] = BYTES_TO_U32_LE( nonce, 4 ); + ctx->state[15] = BYTES_TO_U32_LE( nonce, 8 ); + + mbedtls_platform_zeroize( ctx->keystream8, sizeof( ctx->keystream8 ) ); + + /* Initially, there's no keystream bytes available */ + ctx->keystream_bytes_used = CHACHA20_BLOCK_SIZE_BYTES; + + return( 0 ); +} + +int mbedtls_chacha20_update( mbedtls_chacha20_context *ctx, + size_t size, + const unsigned char *input, + unsigned char *output ) +{ + size_t offset = 0U; + size_t i; + + if( ctx == NULL ) + { + return( MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ); + } + else if( ( size > 0U ) && ( ( input == NULL ) || ( output == NULL ) ) ) + { + /* input and output pointers are allowed to be NULL only if size == 0 */ + return( MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ); + } + + /* Use leftover keystream bytes, if available */ + while( size > 0U && ctx->keystream_bytes_used < CHACHA20_BLOCK_SIZE_BYTES ) + { + output[offset] = input[offset] + ^ ctx->keystream8[ctx->keystream_bytes_used]; + + ctx->keystream_bytes_used++; + offset++; + size--; + } + + /* Process full blocks */ + while( size >= CHACHA20_BLOCK_SIZE_BYTES ) + { + /* Generate new keystream block and increment counter */ + chacha20_block( ctx->state, ctx->keystream8 ); + ctx->state[CHACHA20_CTR_INDEX]++; + + for( i = 0U; i < 64U; i += 8U ) + { + output[offset + i ] = input[offset + i ] ^ ctx->keystream8[i ]; + output[offset + i+1] = input[offset + i+1] ^ ctx->keystream8[i+1]; + output[offset + i+2] = input[offset + i+2] ^ ctx->keystream8[i+2]; + output[offset + i+3] = input[offset + i+3] ^ ctx->keystream8[i+3]; + output[offset + i+4] = input[offset + i+4] ^ ctx->keystream8[i+4]; + output[offset + i+5] = input[offset + i+5] ^ ctx->keystream8[i+5]; + output[offset + i+6] = input[offset + i+6] ^ ctx->keystream8[i+6]; + output[offset + i+7] = input[offset + i+7] ^ ctx->keystream8[i+7]; + } + + offset += CHACHA20_BLOCK_SIZE_BYTES; + size -= CHACHA20_BLOCK_SIZE_BYTES; + } + + /* Last (partial) block */ + if( size > 0U ) + { + /* Generate new keystream block and increment counter */ + chacha20_block( ctx->state, ctx->keystream8 ); + ctx->state[CHACHA20_CTR_INDEX]++; + + for( i = 0U; i < size; i++) + { + output[offset + i] = input[offset + i] ^ ctx->keystream8[i]; + } + + ctx->keystream_bytes_used = size; + + } + + return( 0 ); +} + +int mbedtls_chacha20_crypt( const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter, + size_t data_len, + const unsigned char* input, + unsigned char* output ) +{ + mbedtls_chacha20_context ctx; + int ret; + + mbedtls_chacha20_init( &ctx ); + + ret = mbedtls_chacha20_setkey( &ctx, key ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_chacha20_starts( &ctx, nonce, counter ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_chacha20_update( &ctx, data_len, input, output ); + +cleanup: + mbedtls_chacha20_free( &ctx ); + return( ret ); +} + +#endif /* !MBEDTLS_CHACHA20_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_keys[2][32] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + } +}; + +static const unsigned char test_nonces[2][12] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02 + } +}; + +static const uint32_t test_counters[2] = +{ + 0U, + 1U +}; + +static const unsigned char test_input[2][375] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + { + 0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, + 0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72, + 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20, + 0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, + 0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, + 0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49, + 0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, + 0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, + 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, + 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, + 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, + 0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f + } +}; + +static const unsigned char test_output[2][375] = +{ + { + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, + 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86 + }, + { + 0xa3, 0xfb, 0xf0, 0x7d, 0xf3, 0xfa, 0x2f, 0xde, + 0x4f, 0x37, 0x6c, 0xa2, 0x3e, 0x82, 0x73, 0x70, + 0x41, 0x60, 0x5d, 0x9f, 0x4f, 0x4f, 0x57, 0xbd, + 0x8c, 0xff, 0x2c, 0x1d, 0x4b, 0x79, 0x55, 0xec, + 0x2a, 0x97, 0x94, 0x8b, 0xd3, 0x72, 0x29, 0x15, + 0xc8, 0xf3, 0xd3, 0x37, 0xf7, 0xd3, 0x70, 0x05, + 0x0e, 0x9e, 0x96, 0xd6, 0x47, 0xb7, 0xc3, 0x9f, + 0x56, 0xe0, 0x31, 0xca, 0x5e, 0xb6, 0x25, 0x0d, + 0x40, 0x42, 0xe0, 0x27, 0x85, 0xec, 0xec, 0xfa, + 0x4b, 0x4b, 0xb5, 0xe8, 0xea, 0xd0, 0x44, 0x0e, + 0x20, 0xb6, 0xe8, 0xdb, 0x09, 0xd8, 0x81, 0xa7, + 0xc6, 0x13, 0x2f, 0x42, 0x0e, 0x52, 0x79, 0x50, + 0x42, 0xbd, 0xfa, 0x77, 0x73, 0xd8, 0xa9, 0x05, + 0x14, 0x47, 0xb3, 0x29, 0x1c, 0xe1, 0x41, 0x1c, + 0x68, 0x04, 0x65, 0x55, 0x2a, 0xa6, 0xc4, 0x05, + 0xb7, 0x76, 0x4d, 0x5e, 0x87, 0xbe, 0xa8, 0x5a, + 0xd0, 0x0f, 0x84, 0x49, 0xed, 0x8f, 0x72, 0xd0, + 0xd6, 0x62, 0xab, 0x05, 0x26, 0x91, 0xca, 0x66, + 0x42, 0x4b, 0xc8, 0x6d, 0x2d, 0xf8, 0x0e, 0xa4, + 0x1f, 0x43, 0xab, 0xf9, 0x37, 0xd3, 0x25, 0x9d, + 0xc4, 0xb2, 0xd0, 0xdf, 0xb4, 0x8a, 0x6c, 0x91, + 0x39, 0xdd, 0xd7, 0xf7, 0x69, 0x66, 0xe9, 0x28, + 0xe6, 0x35, 0x55, 0x3b, 0xa7, 0x6c, 0x5c, 0x87, + 0x9d, 0x7b, 0x35, 0xd4, 0x9e, 0xb2, 0xe6, 0x2b, + 0x08, 0x71, 0xcd, 0xac, 0x63, 0x89, 0x39, 0xe2, + 0x5e, 0x8a, 0x1e, 0x0e, 0xf9, 0xd5, 0x28, 0x0f, + 0xa8, 0xca, 0x32, 0x8b, 0x35, 0x1c, 0x3c, 0x76, + 0x59, 0x89, 0xcb, 0xcf, 0x3d, 0xaa, 0x8b, 0x6c, + 0xcc, 0x3a, 0xaf, 0x9f, 0x39, 0x79, 0xc9, 0x2b, + 0x37, 0x20, 0xfc, 0x88, 0xdc, 0x95, 0xed, 0x84, + 0xa1, 0xbe, 0x05, 0x9c, 0x64, 0x99, 0xb9, 0xfd, + 0xa2, 0x36, 0xe7, 0xe8, 0x18, 0xb0, 0x4b, 0x0b, + 0xc3, 0x9c, 0x1e, 0x87, 0x6b, 0x19, 0x3b, 0xfe, + 0x55, 0x69, 0x75, 0x3f, 0x88, 0x12, 0x8c, 0xc0, + 0x8a, 0xaa, 0x9b, 0x63, 0xd1, 0xa1, 0x6f, 0x80, + 0xef, 0x25, 0x54, 0xd7, 0x18, 0x9c, 0x41, 0x1f, + 0x58, 0x69, 0xca, 0x52, 0xc5, 0xb8, 0x3f, 0xa3, + 0x6f, 0xf2, 0x16, 0xb9, 0xc1, 0xd3, 0x00, 0x62, + 0xbe, 0xbc, 0xfd, 0x2d, 0xc5, 0xbc, 0xe0, 0x91, + 0x19, 0x34, 0xfd, 0xa7, 0x9a, 0x86, 0xf6, 0xe6, + 0x98, 0xce, 0xd7, 0x59, 0xc3, 0xff, 0x9b, 0x64, + 0x77, 0x33, 0x8f, 0x3d, 0xa4, 0xf9, 0xcd, 0x85, + 0x14, 0xea, 0x99, 0x82, 0xcc, 0xaf, 0xb3, 0x41, + 0xb2, 0x38, 0x4d, 0xd9, 0x02, 0xf3, 0xd1, 0xab, + 0x7a, 0xc6, 0x1d, 0xd2, 0x9c, 0x6f, 0x21, 0xba, + 0x5b, 0x86, 0x2f, 0x37, 0x30, 0xe3, 0x7c, 0xfd, + 0xc4, 0xfd, 0x80, 0x6c, 0x22, 0xf2, 0x21 + } +}; + +static const size_t test_lengths[2] = +{ + 64U, + 375U +}; + +#define ASSERT( cond, args ) \ + do \ + { \ + if( ! ( cond ) ) \ + { \ + if( verbose != 0 ) \ + mbedtls_printf args; \ + \ + return( -1 ); \ + } \ + } \ + while( 0 ) + +int mbedtls_chacha20_self_test( int verbose ) +{ + unsigned char output[381]; + unsigned i; + int ret; + + for( i = 0U; i < 2U; i++ ) + { + if( verbose != 0 ) + mbedtls_printf( " ChaCha20 test %u ", i ); + + ret = mbedtls_chacha20_crypt( test_keys[i], + test_nonces[i], + test_counters[i], + test_lengths[i], + test_input[i], + output ); + + ASSERT( 0 == ret, ( "error code: %i\n", ret ) ); + + ASSERT( 0 == memcmp( output, test_output[i], test_lengths[i] ), + ( "failed (output)\n" ) ); + + if( verbose != 0 ) + mbedtls_printf( "passed\n" ); + } + + if( verbose != 0 ) + mbedtls_printf( "\n" ); + + return( 0 ); +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* !MBEDTLS_CHACHA20_C */ diff --git a/thirdparty/mbedtls/library/chachapoly.c b/thirdparty/mbedtls/library/chachapoly.c new file mode 100644 index 0000000000..860f877653 --- /dev/null +++ b/thirdparty/mbedtls/library/chachapoly.c @@ -0,0 +1,547 @@ +/** + * \file chachapoly.c + * + * \brief ChaCha20-Poly1305 AEAD construction based on RFC 7539. + * + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + +#include "mbedtls/chachapoly.h" +#include "mbedtls/platform_util.h" + +#include <string.h> + +#if defined(MBEDTLS_SELF_TEST) +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include <stdio.h> +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_C */ +#endif /* MBEDTLS_SELF_TEST */ + +#if !defined(MBEDTLS_CHACHAPOLY_ALT) + +#define CHACHAPOLY_STATE_INIT ( 0 ) +#define CHACHAPOLY_STATE_AAD ( 1 ) +#define CHACHAPOLY_STATE_CIPHERTEXT ( 2 ) /* Encrypting or decrypting */ +#define CHACHAPOLY_STATE_FINISHED ( 3 ) + +/** + * \brief Adds nul bytes to pad the AAD for Poly1305. + * + * \param ctx The ChaCha20-Poly1305 context. + */ +static int chachapoly_pad_aad( mbedtls_chachapoly_context *ctx ) +{ + uint32_t partial_block_len = (uint32_t) ( ctx->aad_len % 16U ); + unsigned char zeroes[15]; + + if( partial_block_len == 0U ) + return( 0 ); + + memset( zeroes, 0, sizeof( zeroes ) ); + + return( mbedtls_poly1305_update( &ctx->poly1305_ctx, + zeroes, + 16U - partial_block_len ) ); +} + +/** + * \brief Adds nul bytes to pad the ciphertext for Poly1305. + * + * \param ctx The ChaCha20-Poly1305 context. + */ +static int chachapoly_pad_ciphertext( mbedtls_chachapoly_context *ctx ) +{ + uint32_t partial_block_len = (uint32_t) ( ctx->ciphertext_len % 16U ); + unsigned char zeroes[15]; + + if( partial_block_len == 0U ) + return( 0 ); + + memset( zeroes, 0, sizeof( zeroes ) ); + return( mbedtls_poly1305_update( &ctx->poly1305_ctx, + zeroes, + 16U - partial_block_len ) ); +} + +void mbedtls_chachapoly_init( mbedtls_chachapoly_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_chacha20_init( &ctx->chacha20_ctx ); + mbedtls_poly1305_init( &ctx->poly1305_ctx ); + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_INIT; + ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT; + } +} + +void mbedtls_chachapoly_free( mbedtls_chachapoly_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_chacha20_free( &ctx->chacha20_ctx ); + mbedtls_poly1305_free( &ctx->poly1305_ctx ); + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_INIT; + ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT; + } +} + +int mbedtls_chachapoly_setkey( mbedtls_chachapoly_context *ctx, + const unsigned char key[32] ) +{ + int ret; + + if( ( ctx == NULL ) || ( key == NULL ) ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + + ret = mbedtls_chacha20_setkey( &ctx->chacha20_ctx, key ); + + return( ret ); +} + +int mbedtls_chachapoly_starts( mbedtls_chachapoly_context *ctx, + const unsigned char nonce[12], + mbedtls_chachapoly_mode_t mode ) +{ + int ret; + unsigned char poly1305_key[64]; + + if( ( ctx == NULL ) || ( nonce == NULL ) ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + + /* Set counter = 0, will be update to 1 when generating Poly1305 key */ + ret = mbedtls_chacha20_starts( &ctx->chacha20_ctx, nonce, 0U ); + if( ret != 0 ) + goto cleanup; + + /* Generate the Poly1305 key by getting the ChaCha20 keystream output with + * counter = 0. This is the same as encrypting a buffer of zeroes. + * Only the first 256-bits (32 bytes) of the key is used for Poly1305. + * The other 256 bits are discarded. + */ + memset( poly1305_key, 0, sizeof( poly1305_key ) ); + ret = mbedtls_chacha20_update( &ctx->chacha20_ctx, sizeof( poly1305_key ), + poly1305_key, poly1305_key ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_poly1305_starts( &ctx->poly1305_ctx, poly1305_key ); + + if( ret == 0 ) + { + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_AAD; + ctx->mode = mode; + } + +cleanup: + mbedtls_platform_zeroize( poly1305_key, 64U ); + return( ret ); +} + +int mbedtls_chachapoly_update_aad( mbedtls_chachapoly_context *ctx, + const unsigned char *aad, + size_t aad_len ) +{ + if( ctx == NULL ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ( aad_len > 0U ) && ( aad == NULL ) ) + { + /* aad pointer is allowed to be NULL if aad_len == 0 */ + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ctx->state != CHACHAPOLY_STATE_AAD ) + { + return( MBEDTLS_ERR_CHACHAPOLY_BAD_STATE ); + } + + ctx->aad_len += aad_len; + + return( mbedtls_poly1305_update( &ctx->poly1305_ctx, aad, aad_len ) ); +} + +int mbedtls_chachapoly_update( mbedtls_chachapoly_context *ctx, + size_t len, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + + if( ctx == NULL ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ( len > 0U ) && ( ( input == NULL ) || ( output == NULL ) ) ) + { + /* input and output pointers are allowed to be NULL if len == 0 */ + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ( ctx->state != CHACHAPOLY_STATE_AAD ) && + ( ctx->state != CHACHAPOLY_STATE_CIPHERTEXT ) ) + { + return( MBEDTLS_ERR_CHACHAPOLY_BAD_STATE ); + } + + if( ctx->state == CHACHAPOLY_STATE_AAD ) + { + ctx->state = CHACHAPOLY_STATE_CIPHERTEXT; + + ret = chachapoly_pad_aad( ctx ); + if( ret != 0 ) + return( ret ); + } + + ctx->ciphertext_len += len; + + if( ctx->mode == MBEDTLS_CHACHAPOLY_ENCRYPT ) + { + ret = mbedtls_chacha20_update( &ctx->chacha20_ctx, len, input, output ); + if( ret != 0 ) + return( ret ); + + ret = mbedtls_poly1305_update( &ctx->poly1305_ctx, output, len ); + if( ret != 0 ) + return( ret ); + } + else /* DECRYPT */ + { + ret = mbedtls_poly1305_update( &ctx->poly1305_ctx, input, len ); + if( ret != 0 ) + return( ret ); + + ret = mbedtls_chacha20_update( &ctx->chacha20_ctx, len, input, output ); + if( ret != 0 ) + return( ret ); + } + + return( 0 ); +} + +int mbedtls_chachapoly_finish( mbedtls_chachapoly_context *ctx, + unsigned char mac[16] ) +{ + int ret; + unsigned char len_block[16]; + + if( ( ctx == NULL ) || ( mac == NULL ) ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ctx->state == CHACHAPOLY_STATE_INIT ) + { + return( MBEDTLS_ERR_CHACHAPOLY_BAD_STATE ); + } + + if( ctx->state == CHACHAPOLY_STATE_AAD ) + { + ret = chachapoly_pad_aad( ctx ); + if( ret != 0 ) + return( ret ); + } + else if( ctx->state == CHACHAPOLY_STATE_CIPHERTEXT ) + { + ret = chachapoly_pad_ciphertext( ctx ); + if( ret != 0 ) + return( ret ); + } + + ctx->state = CHACHAPOLY_STATE_FINISHED; + + /* The lengths of the AAD and ciphertext are processed by + * Poly1305 as the final 128-bit block, encoded as little-endian integers. + */ + len_block[ 0] = (unsigned char)( ctx->aad_len ); + len_block[ 1] = (unsigned char)( ctx->aad_len >> 8 ); + len_block[ 2] = (unsigned char)( ctx->aad_len >> 16 ); + len_block[ 3] = (unsigned char)( ctx->aad_len >> 24 ); + len_block[ 4] = (unsigned char)( ctx->aad_len >> 32 ); + len_block[ 5] = (unsigned char)( ctx->aad_len >> 40 ); + len_block[ 6] = (unsigned char)( ctx->aad_len >> 48 ); + len_block[ 7] = (unsigned char)( ctx->aad_len >> 56 ); + len_block[ 8] = (unsigned char)( ctx->ciphertext_len ); + len_block[ 9] = (unsigned char)( ctx->ciphertext_len >> 8 ); + len_block[10] = (unsigned char)( ctx->ciphertext_len >> 16 ); + len_block[11] = (unsigned char)( ctx->ciphertext_len >> 24 ); + len_block[12] = (unsigned char)( ctx->ciphertext_len >> 32 ); + len_block[13] = (unsigned char)( ctx->ciphertext_len >> 40 ); + len_block[14] = (unsigned char)( ctx->ciphertext_len >> 48 ); + len_block[15] = (unsigned char)( ctx->ciphertext_len >> 56 ); + + ret = mbedtls_poly1305_update( &ctx->poly1305_ctx, len_block, 16U ); + if( ret != 0 ) + return( ret ); + + ret = mbedtls_poly1305_finish( &ctx->poly1305_ctx, mac ); + + return( ret ); +} + +static int chachapoly_crypt_and_tag( mbedtls_chachapoly_context *ctx, + mbedtls_chachapoly_mode_t mode, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16] ) +{ + int ret; + + ret = mbedtls_chachapoly_starts( ctx, nonce, mode ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_chachapoly_update_aad( ctx, aad, aad_len ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_chachapoly_update( ctx, length, input, output ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_chachapoly_finish( ctx, tag ); + +cleanup: + return( ret ); +} + +int mbedtls_chachapoly_encrypt_and_tag( mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16] ) +{ + return( chachapoly_crypt_and_tag( ctx, MBEDTLS_CHACHAPOLY_ENCRYPT, + length, nonce, aad, aad_len, + input, output, tag ) ); +} + +int mbedtls_chachapoly_auth_decrypt( mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char tag[16], + const unsigned char *input, + unsigned char *output ) +{ + int ret; + unsigned char check_tag[16]; + size_t i; + int diff; + + if( tag == NULL ) + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + + if( ( ret = chachapoly_crypt_and_tag( ctx, + MBEDTLS_CHACHAPOLY_DECRYPT, length, nonce, + aad, aad_len, input, output, check_tag ) ) != 0 ) + { + return( ret ); + } + + /* Check tag in "constant-time" */ + for( diff = 0, i = 0; i < sizeof( check_tag ); i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) + { + mbedtls_platform_zeroize( output, length ); + return( MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED ); + } + + return( 0 ); +} + +#endif /* MBEDTLS_CHACHAPOLY_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_key[1][32] = +{ + { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f + } +}; + +static const unsigned char test_nonce[1][12] = +{ + { + 0x07, 0x00, 0x00, 0x00, /* 32-bit common part */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 /* 64-bit IV */ + } +}; + +static const unsigned char test_aad[1][12] = +{ + { + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7 + } +}; + +static const size_t test_aad_len[1] = +{ + 12U +}; + +static const unsigned char test_input[1][114] = +{ + { + 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, + 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, + 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x74, 0x2e + } +}; + +static const unsigned char test_output[1][114] = +{ + { + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, + 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, + 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, + 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, + 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, + 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, + 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, + 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, + 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, + 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, + 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, + 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, + 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, + 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, + 0x61, 0x16 + } +}; + +static const size_t test_input_len[1] = +{ + 114U +}; + +static const unsigned char test_mac[1][16] = +{ + { + 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, + 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 + } +}; + +#define ASSERT( cond, args ) \ + do \ + { \ + if( ! ( cond ) ) \ + { \ + if( verbose != 0 ) \ + mbedtls_printf args; \ + \ + return( -1 ); \ + } \ + } \ + while( 0 ) + +int mbedtls_chachapoly_self_test( int verbose ) +{ + mbedtls_chachapoly_context ctx; + unsigned i; + int ret; + unsigned char output[200]; + unsigned char mac[16]; + + for( i = 0U; i < 1U; i++ ) + { + if( verbose != 0 ) + mbedtls_printf( " ChaCha20-Poly1305 test %u ", i ); + + mbedtls_chachapoly_init( &ctx ); + + ret = mbedtls_chachapoly_setkey( &ctx, test_key[i] ); + ASSERT( 0 == ret, ( "setkey() error code: %i\n", ret ) ); + + ret = mbedtls_chachapoly_encrypt_and_tag( &ctx, + test_input_len[i], + test_nonce[i], + test_aad[i], + test_aad_len[i], + test_input[i], + output, + mac ); + + ASSERT( 0 == ret, ( "crypt_and_tag() error code: %i\n", ret ) ); + + ASSERT( 0 == memcmp( output, test_output[i], test_input_len[i] ), + ( "failure (wrong output)\n" ) ); + + ASSERT( 0 == memcmp( mac, test_mac[i], 16U ), + ( "failure (wrong MAC)\n" ) ); + + mbedtls_chachapoly_free( &ctx ); + + if( verbose != 0 ) + mbedtls_printf( "passed\n" ); + } + + if( verbose != 0 ) + mbedtls_printf( "\n" ); + + return( 0 ); +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_CHACHAPOLY_C */ diff --git a/thirdparty/mbedtls/library/cipher.c b/thirdparty/mbedtls/library/cipher.c index a5cd61cdf3..7ae6c4ac5d 100644 --- a/thirdparty/mbedtls/library/cipher.c +++ b/thirdparty/mbedtls/library/cipher.c @@ -38,6 +38,10 @@ #include <stdlib.h> #include <string.h> +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + #if defined(MBEDTLS_GCM_C) #include "mbedtls/gcm.h" #endif @@ -46,6 +50,10 @@ #include "mbedtls/ccm.h" #endif +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + #if defined(MBEDTLS_CMAC_C) #include "mbedtls/cmac.h" #endif @@ -57,9 +65,25 @@ #define mbedtls_free free #endif -#if defined(MBEDTLS_ARC4_C) || defined(MBEDTLS_CIPHER_NULL_CIPHER) -#define MBEDTLS_CIPHER_MODE_STREAM -#endif +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) +/* Compare the contents of two buffers in constant time. + * Returns 0 if the contents are bitwise identical, otherwise returns + * a non-zero value. + * This is currently only used by GCM and ChaCha20+Poly1305. + */ +static int mbedtls_constant_time_memcmp( const void *v1, const void *v2, size_t len ) +{ + const unsigned char *p1 = (const unsigned char*) v1; + const unsigned char *p2 = (const unsigned char*) v2; + size_t i; + unsigned char diff; + + for( diff = 0, i = 0; i < len; i++ ) + diff |= p1[i] ^ p2[i]; + + return (int)diff; +} +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ static int supported_init = 0; @@ -191,10 +215,11 @@ int mbedtls_cipher_setkey( mbedtls_cipher_context_t *ctx, const unsigned char *k ctx->operation = operation; /* - * For CFB and CTR mode always use the encryption key schedule + * For OFB, CFB and CTR mode always use the encryption key schedule */ if( MBEDTLS_ENCRYPT == operation || MBEDTLS_MODE_CFB == ctx->cipher_info->mode || + MBEDTLS_MODE_OFB == ctx->cipher_info->mode || MBEDTLS_MODE_CTR == ctx->cipher_info->mode ) { return ctx->cipher_info->base->setkey_enc_func( ctx->cipher_ctx, key, @@ -231,6 +256,18 @@ int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx, return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); } +#if defined(MBEDTLS_CHACHA20_C) + if ( ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20 ) + { + if ( 0 != mbedtls_chacha20_starts( (mbedtls_chacha20_context*)ctx->cipher_ctx, + iv, + 0U ) ) /* Initial counter value */ + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + } +#endif + memcpy( ctx->iv, iv, actual_iv_size ); ctx->iv_size = actual_iv_size; @@ -247,22 +284,45 @@ int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx ) return( 0 ); } -#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx, const unsigned char *ad, size_t ad_len ) { if( NULL == ctx || NULL == ctx->cipher_info ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); +#if defined(MBEDTLS_GCM_C) if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode ) { return mbedtls_gcm_starts( (mbedtls_gcm_context *) ctx->cipher_ctx, ctx->operation, ctx->iv, ctx->iv_size, ad, ad_len ); } +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) + { + int result; + mbedtls_chachapoly_mode_t mode; + + mode = ( ctx->operation == MBEDTLS_ENCRYPT ) + ? MBEDTLS_CHACHAPOLY_ENCRYPT + : MBEDTLS_CHACHAPOLY_DECRYPT; + + result = mbedtls_chachapoly_starts( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + ctx->iv, + mode ); + if ( result != 0 ) + return( result ); + + return mbedtls_chachapoly_update_aad( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + ad, ad_len ); + } +#endif return( 0 ); } -#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen ) @@ -303,6 +363,15 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i } #endif +#if defined(MBEDTLS_CHACHAPOLY_C) + if ( ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) + { + *olen = ilen; + return mbedtls_chachapoly_update( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + ilen, input, output ); + } +#endif + if ( 0 == block_size ) { return MBEDTLS_ERR_CIPHER_INVALID_CONTEXT; @@ -424,6 +493,21 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i } #endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) + if( ctx->cipher_info->mode == MBEDTLS_MODE_OFB ) + { + if( 0 != ( ret = ctx->cipher_info->base->ofb_func( ctx->cipher_ctx, + ilen, &ctx->unprocessed_len, ctx->iv, input, output ) ) ) + { + return( ret ); + } + + *olen = ilen; + + return( 0 ); + } +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) if( ctx->cipher_info->mode == MBEDTLS_MODE_CTR ) { @@ -440,6 +524,27 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i } #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) + if( ctx->cipher_info->mode == MBEDTLS_MODE_XTS ) + { + if( ctx->unprocessed_len > 0 ) { + /* We can only process an entire data unit at a time. */ + return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ); + } + + ret = ctx->cipher_info->base->xts_func( ctx->cipher_ctx, + ctx->operation, ilen, ctx->iv, input, output ); + if( ret != 0 ) + { + return( ret ); + } + + *olen = ilen; + + return( 0 ); + } +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #if defined(MBEDTLS_CIPHER_MODE_STREAM) if( ctx->cipher_info->mode == MBEDTLS_MODE_STREAM ) { @@ -639,13 +744,21 @@ int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx, *olen = 0; if( MBEDTLS_MODE_CFB == ctx->cipher_info->mode || + MBEDTLS_MODE_OFB == ctx->cipher_info->mode || MBEDTLS_MODE_CTR == ctx->cipher_info->mode || MBEDTLS_MODE_GCM == ctx->cipher_info->mode || + MBEDTLS_MODE_XTS == ctx->cipher_info->mode || MBEDTLS_MODE_STREAM == ctx->cipher_info->mode ) { return( 0 ); } + if ( ( MBEDTLS_CIPHER_CHACHA20 == ctx->cipher_info->type ) || + ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) ) + { + return( 0 ); + } + if( MBEDTLS_MODE_ECB == ctx->cipher_info->mode ) { if( ctx->unprocessed_len != 0 ) @@ -757,7 +870,7 @@ int mbedtls_cipher_set_padding_mode( mbedtls_cipher_context_t *ctx, mbedtls_ciph } #endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ -#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, unsigned char *tag, size_t tag_len ) { @@ -767,8 +880,22 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, if( MBEDTLS_ENCRYPT != ctx->operation ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); +#if defined(MBEDTLS_GCM_C) if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode ) return mbedtls_gcm_finish( (mbedtls_gcm_context *) ctx->cipher_ctx, tag, tag_len ); +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) + { + /* Don't allow truncated MAC for Poly1305 */ + if ( tag_len != 16U ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + return mbedtls_chachapoly_finish( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + tag ); + } +#endif return( 0 ); } @@ -776,6 +903,7 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx, const unsigned char *tag, size_t tag_len ) { + unsigned char check_tag[16]; int ret; if( NULL == ctx || NULL == ctx->cipher_info || @@ -784,12 +912,9 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx, return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); } +#if defined(MBEDTLS_GCM_C) if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode ) { - unsigned char check_tag[16]; - size_t i; - int diff; - if( tag_len > sizeof( check_tag ) ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); @@ -800,18 +925,38 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx, } /* Check the tag in "constant-time" */ - for( diff = 0, i = 0; i < tag_len; i++ ) - diff |= tag[i] ^ check_tag[i]; + if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 ) + return( MBEDTLS_ERR_CIPHER_AUTH_FAILED ); + + return( 0 ); + } +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) + { + /* Don't allow truncated MAC for Poly1305 */ + if ( tag_len != sizeof( check_tag ) ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); - if( diff != 0 ) + ret = mbedtls_chachapoly_finish( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + check_tag ); + if ( ret != 0 ) + { + return( ret ); + } + + /* Check the tag in "constant-time" */ + if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 ) return( MBEDTLS_ERR_CIPHER_AUTH_FAILED ); return( 0 ); } +#endif /* MBEDTLS_CHACHAPOLY_C */ return( 0 ); } -#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ /* * Packet-oriented wrapper for non-AEAD modes @@ -870,6 +1015,21 @@ int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx, tag, tag_len ) ); } #endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) + { + /* ChachaPoly has fixed length nonce and MAC (tag) */ + if ( ( iv_len != ctx->cipher_info->iv_size ) || + ( tag_len != 16U ) ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + *olen = ilen; + return( mbedtls_chachapoly_encrypt_and_tag( ctx->cipher_ctx, + ilen, iv, ad, ad_len, input, output, tag ) ); + } +#endif /* MBEDTLS_CHACHAPOLY_C */ return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ); } @@ -916,6 +1076,28 @@ int mbedtls_cipher_auth_decrypt( mbedtls_cipher_context_t *ctx, return( ret ); } #endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) + { + int ret; + + /* ChachaPoly has fixed length nonce and MAC (tag) */ + if ( ( iv_len != ctx->cipher_info->iv_size ) || + ( tag_len != 16U ) ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + *olen = ilen; + ret = mbedtls_chachapoly_auth_decrypt( ctx->cipher_ctx, ilen, + iv, ad, ad_len, tag, input, output ); + + if( ret == MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED ) + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + + return( ret ); + } +#endif /* MBEDTLS_CHACHAPOLY_C */ return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ); } diff --git a/thirdparty/mbedtls/library/cipher_wrap.c b/thirdparty/mbedtls/library/cipher_wrap.c index a9ef8195ca..893490acc8 100644 --- a/thirdparty/mbedtls/library/cipher_wrap.c +++ b/thirdparty/mbedtls/library/cipher_wrap.c @@ -33,6 +33,10 @@ #include "mbedtls/cipher_internal.h" +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + #if defined(MBEDTLS_AES_C) #include "mbedtls/aes.h" #endif @@ -57,6 +61,10 @@ #include "mbedtls/blowfish.h" #endif +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + #if defined(MBEDTLS_GCM_C) #include "mbedtls/gcm.h" #endif @@ -142,6 +150,15 @@ static int aes_crypt_cfb128_wrap( void *ctx, mbedtls_operation_t operation, } #endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) +static int aes_crypt_ofb_wrap( void *ctx, size_t length, size_t *iv_off, + unsigned char *iv, const unsigned char *input, unsigned char *output ) +{ + return mbedtls_aes_crypt_ofb( (mbedtls_aes_context *) ctx, length, iv_off, + iv, input, output ); +} +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) static int aes_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off, unsigned char *nonce_counter, unsigned char *stream_block, @@ -152,6 +169,33 @@ static int aes_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off, } #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int aes_crypt_xts_wrap( void *ctx, mbedtls_operation_t operation, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output ) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + int mode; + + switch( operation ) + { + case MBEDTLS_ENCRYPT: + mode = MBEDTLS_AES_ENCRYPT; + break; + case MBEDTLS_DECRYPT: + mode = MBEDTLS_AES_DECRYPT; + break; + default: + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return mbedtls_aes_crypt_xts( xts_ctx, mode, length, + data_unit, input, output ); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + static int aes_setkey_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_bitlen ) { @@ -191,9 +235,15 @@ static const mbedtls_cipher_base_t aes_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) aes_crypt_cfb128_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + aes_crypt_ofb_wrap, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) aes_crypt_ctr_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -306,6 +356,41 @@ static const mbedtls_cipher_info_t aes_256_cfb128_info = { }; #endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) +static const mbedtls_cipher_info_t aes_128_ofb_info = { + MBEDTLS_CIPHER_AES_128_OFB, + MBEDTLS_MODE_OFB, + 128, + "AES-128-OFB", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ofb_info = { + MBEDTLS_CIPHER_AES_192_OFB, + MBEDTLS_MODE_OFB, + 192, + "AES-192-OFB", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ofb_info = { + MBEDTLS_CIPHER_AES_256_OFB, + MBEDTLS_MODE_OFB, + 256, + "AES-256-OFB", + 16, + 0, + 16, + &aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + #if defined(MBEDTLS_CIPHER_MODE_CTR) static const mbedtls_cipher_info_t aes_128_ctr_info = { MBEDTLS_CIPHER_AES_128_CTR, @@ -341,6 +426,92 @@ static const mbedtls_cipher_info_t aes_256_ctr_info = { }; #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int xts_aes_setkey_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_bitlen ) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + return( mbedtls_aes_xts_setkey_enc( xts_ctx, key, key_bitlen ) ); +} + +static int xts_aes_setkey_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_bitlen ) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + return( mbedtls_aes_xts_setkey_dec( xts_ctx, key, key_bitlen ) ); +} + +static void *xts_aes_ctx_alloc( void ) +{ + mbedtls_aes_xts_context *xts_ctx = mbedtls_calloc( 1, sizeof( *xts_ctx ) ); + + if( xts_ctx != NULL ) + mbedtls_aes_xts_init( xts_ctx ); + + return( xts_ctx ); +} + +static void xts_aes_ctx_free( void *ctx ) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + + if( xts_ctx == NULL ) + return; + + mbedtls_aes_xts_free( xts_ctx ); + mbedtls_free( xts_ctx ); +} + +static const mbedtls_cipher_base_t xts_aes_info = { + MBEDTLS_CIPHER_ID_AES, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + aes_crypt_xts_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + xts_aes_setkey_enc_wrap, + xts_aes_setkey_dec_wrap, + xts_aes_ctx_alloc, + xts_aes_ctx_free +}; + +static const mbedtls_cipher_info_t aes_128_xts_info = { + MBEDTLS_CIPHER_AES_128_XTS, + MBEDTLS_MODE_XTS, + 256, + "AES-128-XTS", + 16, + 0, + 16, + &xts_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_xts_info = { + MBEDTLS_CIPHER_AES_256_XTS, + MBEDTLS_MODE_XTS, + 512, + "AES-256-XTS", + 16, + 0, + 16, + &xts_aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + #if defined(MBEDTLS_GCM_C) static int gcm_aes_setkey_wrap( void *ctx, const unsigned char *key, unsigned int key_bitlen ) @@ -358,9 +529,15 @@ static const mbedtls_cipher_base_t gcm_aes_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -421,9 +598,15 @@ static const mbedtls_cipher_base_t ccm_aes_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -548,9 +731,15 @@ static const mbedtls_cipher_base_t camellia_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) camellia_crypt_cfb128_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) camellia_crypt_ctr_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -715,9 +904,15 @@ static const mbedtls_cipher_base_t gcm_camellia_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -778,9 +973,15 @@ static const mbedtls_cipher_base_t ccm_camellia_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -906,9 +1107,15 @@ static const mbedtls_cipher_base_t aria_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) aria_crypt_cfb128_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) aria_crypt_ctr_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1073,9 +1280,15 @@ static const mbedtls_cipher_base_t gcm_aria_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1136,9 +1349,15 @@ static const mbedtls_cipher_base_t ccm_aria_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1312,9 +1531,15 @@ static const mbedtls_cipher_base_t des_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1357,9 +1582,15 @@ static const mbedtls_cipher_base_t des_ede_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1402,9 +1633,15 @@ static const mbedtls_cipher_base_t des_ede3_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1511,9 +1748,15 @@ static const mbedtls_cipher_base_t blowfish_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) blowfish_crypt_cfb64_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) blowfish_crypt_ctr_wrap, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) NULL, #endif @@ -1621,9 +1864,15 @@ static const mbedtls_cipher_base_t arc4_base_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) arc4_crypt_stream_wrap, #endif @@ -1645,6 +1894,162 @@ static const mbedtls_cipher_info_t arc4_128_info = { }; #endif /* MBEDTLS_ARC4_C */ +#if defined(MBEDTLS_CHACHA20_C) + +static int chacha20_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_bitlen ) +{ + if( key_bitlen != 256U ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + if ( 0 != mbedtls_chacha20_setkey( (mbedtls_chacha20_context*)ctx, key ) ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + return( 0 ); +} + +static int chacha20_stream_wrap( void *ctx, size_t length, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + + ret = mbedtls_chacha20_update( ctx, length, input, output ); + if( ret == MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + return( ret ); +} + +static void * chacha20_ctx_alloc( void ) +{ + mbedtls_chacha20_context *ctx; + ctx = mbedtls_calloc( 1, sizeof( mbedtls_chacha20_context ) ); + + if( ctx == NULL ) + return( NULL ); + + mbedtls_chacha20_init( ctx ); + + return( ctx ); +} + +static void chacha20_ctx_free( void *ctx ) +{ + mbedtls_chacha20_free( (mbedtls_chacha20_context *) ctx ); + mbedtls_free( ctx ); +} + +static const mbedtls_cipher_base_t chacha20_base_info = { + MBEDTLS_CIPHER_ID_CHACHA20, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + chacha20_stream_wrap, +#endif + chacha20_setkey_wrap, + chacha20_setkey_wrap, + chacha20_ctx_alloc, + chacha20_ctx_free +}; +static const mbedtls_cipher_info_t chacha20_info = { + MBEDTLS_CIPHER_CHACHA20, + MBEDTLS_MODE_STREAM, + 256, + "CHACHA20", + 12, + 0, + 1, + &chacha20_base_info +}; +#endif /* MBEDTLS_CHACHA20_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + +static int chachapoly_setkey_wrap( void *ctx, + const unsigned char *key, + unsigned int key_bitlen ) +{ + if( key_bitlen != 256U ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + if ( 0 != mbedtls_chachapoly_setkey( (mbedtls_chachapoly_context*)ctx, key ) ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + return( 0 ); +} + +static void * chachapoly_ctx_alloc( void ) +{ + mbedtls_chachapoly_context *ctx; + ctx = mbedtls_calloc( 1, sizeof( mbedtls_chachapoly_context ) ); + + if( ctx == NULL ) + return( NULL ); + + mbedtls_chachapoly_init( ctx ); + + return( ctx ); +} + +static void chachapoly_ctx_free( void *ctx ) +{ + mbedtls_chachapoly_free( (mbedtls_chachapoly_context *) ctx ); + mbedtls_free( ctx ); +} + +static const mbedtls_cipher_base_t chachapoly_base_info = { + MBEDTLS_CIPHER_ID_CHACHA20, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + chachapoly_setkey_wrap, + chachapoly_setkey_wrap, + chachapoly_ctx_alloc, + chachapoly_ctx_free +}; +static const mbedtls_cipher_info_t chachapoly_info = { + MBEDTLS_CIPHER_CHACHA20_POLY1305, + MBEDTLS_MODE_CHACHAPOLY, + 256, + "CHACHA20-POLY1305", + 12, + 0, + 1, + &chachapoly_base_info +}; +#endif /* MBEDTLS_CHACHAPOLY_C */ + #if defined(MBEDTLS_CIPHER_NULL_CIPHER) static int null_crypt_stream( void *ctx, size_t length, const unsigned char *input, @@ -1684,9 +2089,15 @@ static const mbedtls_cipher_base_t null_base_info = { #if defined(MBEDTLS_CIPHER_MODE_CFB) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) NULL, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif #if defined(MBEDTLS_CIPHER_MODE_STREAM) null_crypt_stream, #endif @@ -1724,11 +2135,20 @@ const mbedtls_cipher_definition_t mbedtls_cipher_definitions[] = { MBEDTLS_CIPHER_AES_192_CFB128, &aes_192_cfb128_info }, { MBEDTLS_CIPHER_AES_256_CFB128, &aes_256_cfb128_info }, #endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + { MBEDTLS_CIPHER_AES_128_OFB, &aes_128_ofb_info }, + { MBEDTLS_CIPHER_AES_192_OFB, &aes_192_ofb_info }, + { MBEDTLS_CIPHER_AES_256_OFB, &aes_256_ofb_info }, +#endif #if defined(MBEDTLS_CIPHER_MODE_CTR) { MBEDTLS_CIPHER_AES_128_CTR, &aes_128_ctr_info }, { MBEDTLS_CIPHER_AES_192_CTR, &aes_192_ctr_info }, { MBEDTLS_CIPHER_AES_256_CTR, &aes_256_ctr_info }, #endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + { MBEDTLS_CIPHER_AES_128_XTS, &aes_128_xts_info }, + { MBEDTLS_CIPHER_AES_256_XTS, &aes_256_xts_info }, +#endif #if defined(MBEDTLS_GCM_C) { MBEDTLS_CIPHER_AES_128_GCM, &aes_128_gcm_info }, { MBEDTLS_CIPHER_AES_192_GCM, &aes_192_gcm_info }, @@ -1831,6 +2251,14 @@ const mbedtls_cipher_definition_t mbedtls_cipher_definitions[] = #endif #endif /* MBEDTLS_DES_C */ +#if defined(MBEDTLS_CHACHA20_C) + { MBEDTLS_CIPHER_CHACHA20, &chacha20_info }, +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + { MBEDTLS_CIPHER_CHACHA20_POLY1305, &chachapoly_info }, +#endif + #if defined(MBEDTLS_CIPHER_NULL_CIPHER) { MBEDTLS_CIPHER_NULL, &null_cipher_info }, #endif /* MBEDTLS_CIPHER_NULL_CIPHER */ diff --git a/thirdparty/mbedtls/library/cmac.c b/thirdparty/mbedtls/library/cmac.c index 4d7a1f1693..5d101e1c7d 100644 --- a/thirdparty/mbedtls/library/cmac.c +++ b/thirdparty/mbedtls/library/cmac.c @@ -828,6 +828,7 @@ static int cmac_test_subkeys( int verbose, mbedtls_cipher_free( &ctx ); } + ret = 0; goto exit; cleanup: @@ -883,6 +884,7 @@ static int cmac_test_wth_cipher( int verbose, if( verbose != 0 ) mbedtls_printf( "passed\n" ); } + ret = 0; exit: return( ret ); diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c index cefe882d2a..f44a753f4d 100644 --- a/thirdparty/mbedtls/library/entropy_poll.c +++ b/thirdparty/mbedtls/library/entropy_poll.c @@ -19,19 +19,25 @@ * This file is part of mbed TLS (https://tls.mbed.org) */ +#if defined(__linux__) +/* Ensure that syscall() is available even when compiling with -std=c99 */ +#define _GNU_SOURCE +#endif + #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else #include MBEDTLS_CONFIG_FILE #endif +#include <string.h> + #if defined(MBEDTLS_ENTROPY_C) #include "mbedtls/entropy.h" #include "mbedtls/entropy_poll.h" #if defined(MBEDTLS_TIMING_C) -#include <string.h> #include "mbedtls/timing.h" #endif #if defined(MBEDTLS_HAVEGE_C) @@ -44,7 +50,8 @@ #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ - !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) #error "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in config.h" #endif diff --git a/thirdparty/mbedtls/library/entropy_poll.c.orig b/thirdparty/mbedtls/library/entropy_poll.c.orig new file mode 100644 index 0000000000..040aa117dc --- /dev/null +++ b/thirdparty/mbedtls/library/entropy_poll.c.orig @@ -0,0 +1,275 @@ +/* + * Platform-specific and custom entropy polling functions + * + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if defined(__linux__) +/* Ensure that syscall() is available even when compiling with -std=c99 */ +#define _GNU_SOURCE +#endif + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include <string.h> + +#if defined(MBEDTLS_ENTROPY_C) + +#include "mbedtls/entropy.h" +#include "mbedtls/entropy_poll.h" + +#if defined(MBEDTLS_TIMING_C) +#include "mbedtls/timing.h" +#endif +#if defined(MBEDTLS_HAVEGE_C) +#include "mbedtls/havege.h" +#endif +#if defined(MBEDTLS_ENTROPY_NV_SEED) +#include "mbedtls/platform.h" +#endif + +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) + +#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) +#error "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in config.h" +#endif + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0400 +#endif +#include <windows.h> +#include <wincrypt.h> + +int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len, + size_t *olen ) +{ + HCRYPTPROV provider; + ((void) data); + *olen = 0; + + if( CryptAcquireContext( &provider, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE ) + { + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + } + + if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE ) + { + CryptReleaseContext( provider, 0 ); + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + } + + CryptReleaseContext( provider, 0 ); + *olen = len; + + return( 0 ); +} +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +/* + * Test for Linux getrandom() support. + * Since there is no wrapper in the libc yet, use the generic syscall wrapper + * available in GNU libc and compatible libc's (eg uClibc). + */ +#if defined(__linux__) && defined(__GLIBC__) +#include <unistd.h> +#include <sys/syscall.h> +#if defined(SYS_getrandom) +#define HAVE_GETRANDOM + +static int getrandom_wrapper( void *buf, size_t buflen, unsigned int flags ) +{ + /* MemSan cannot understand that the syscall writes to the buffer */ +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset( buf, 0, buflen ); +#endif +#endif + + return( syscall( SYS_getrandom, buf, buflen, flags ) ); +} + +#include <sys/utsname.h> +/* Check if version is at least 3.17.0 */ +static int check_version_3_17_plus( void ) +{ + int minor; + struct utsname un; + const char *ver; + + /* Get version information */ + uname(&un); + ver = un.release; + + /* Check major version; assume a single digit */ + if( ver[0] < '3' || ver[0] > '9' || ver [1] != '.' ) + return( -1 ); + + if( ver[0] - '0' > 3 ) + return( 0 ); + + /* Ok, so now we know major == 3, check minor. + * Assume 1 or 2 digits. */ + if( ver[2] < '0' || ver[2] > '9' ) + return( -1 ); + + minor = ver[2] - '0'; + + if( ver[3] >= '0' && ver[3] <= '9' ) + minor = 10 * minor + ver[3] - '0'; + else if( ver [3] != '.' ) + return( -1 ); + + if( minor < 17 ) + return( -1 ); + + return( 0 ); +} +static int has_getrandom = -1; +#endif /* SYS_getrandom */ +#endif /* __linux__ */ + +#include <stdio.h> + +int mbedtls_platform_entropy_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + FILE *file; + size_t read_len; + ((void) data); + +#if defined(HAVE_GETRANDOM) + if( has_getrandom == -1 ) + has_getrandom = ( check_version_3_17_plus() == 0 ); + + if( has_getrandom ) + { + int ret; + + if( ( ret = getrandom_wrapper( output, len, 0 ) ) < 0 ) + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + + *olen = ret; + return( 0 ); + } +#endif /* HAVE_GETRANDOM */ + + *olen = 0; + + file = fopen( "/dev/urandom", "rb" ); + if( file == NULL ) + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + + read_len = fread( output, 1, len, file ); + if( read_len != len ) + { + fclose( file ); + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + } + + fclose( file ); + *olen = len; + + return( 0 ); +} +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +#endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */ + +#if defined(MBEDTLS_TEST_NULL_ENTROPY) +int mbedtls_null_entropy_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + ((void) data); + ((void) output); + *olen = 0; + + if( len < sizeof(unsigned char) ) + return( 0 ); + + *olen = sizeof(unsigned char); + + return( 0 ); +} +#endif + +#if defined(MBEDTLS_TIMING_C) +int mbedtls_hardclock_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + unsigned long timer = mbedtls_timing_hardclock(); + ((void) data); + *olen = 0; + + if( len < sizeof(unsigned long) ) + return( 0 ); + + memcpy( output, &timer, sizeof(unsigned long) ); + *olen = sizeof(unsigned long); + + return( 0 ); +} +#endif /* MBEDTLS_TIMING_C */ + +#if defined(MBEDTLS_HAVEGE_C) +int mbedtls_havege_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + mbedtls_havege_state *hs = (mbedtls_havege_state *) data; + *olen = 0; + + if( mbedtls_havege_random( hs, output, len ) != 0 ) + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + + *olen = len; + + return( 0 ); +} +#endif /* MBEDTLS_HAVEGE_C */ + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +int mbedtls_nv_seed_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE; + ((void) data); + + memset( buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE ); + + if( mbedtls_nv_seed_read( buf, MBEDTLS_ENTROPY_BLOCK_SIZE ) < 0 ) + return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); + + if( len < use_len ) + use_len = len; + + memcpy( output, buf, use_len ); + *olen = use_len; + + return( 0 ); +} +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#endif /* MBEDTLS_ENTROPY_C */ diff --git a/thirdparty/mbedtls/library/error.c b/thirdparty/mbedtls/library/error.c index 8818054c56..774244b454 100644 --- a/thirdparty/mbedtls/library/error.c +++ b/thirdparty/mbedtls/library/error.c @@ -73,6 +73,14 @@ #include "mbedtls/ccm.h" #endif +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + #if defined(MBEDTLS_CIPHER_C) #include "mbedtls/cipher.h" #endif @@ -105,6 +113,10 @@ #include "mbedtls/gcm.h" #endif +#if defined(MBEDTLS_HKDF_C) +#include "mbedtls/hkdf.h" +#endif + #if defined(MBEDTLS_HMAC_DRBG_C) #include "mbedtls/hmac_drbg.h" #endif @@ -153,6 +165,10 @@ #include "mbedtls/pkcs5.h" #endif +#if defined(MBEDTLS_POLY1305_C) +#include "mbedtls/poly1305.h" +#endif + #if defined(MBEDTLS_RIPEMD160_C) #include "mbedtls/ripemd160.h" #endif @@ -497,6 +513,8 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "SSL - Couldn't set the hash for verifying CertificateVerify" ); if( use_ret == -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING) ) mbedtls_snprintf( buf, buflen, "SSL - Internal-only message signaling that further message-processing should be done" ); + if( use_ret == -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) ) + mbedtls_snprintf( buf, buflen, "SSL - The asynchronous operation is not completed yet" ); #endif /* MBEDTLS_SSL_TLS_C */ #if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) @@ -670,6 +688,22 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "CCM - CCM hardware accelerator failed" ); #endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHA20_C) + if( use_ret == -(MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA) ) + mbedtls_snprintf( buf, buflen, "CHACHA20 - Invalid input parameter(s)" ); + if( use_ret == -(MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE) ) + mbedtls_snprintf( buf, buflen, "CHACHA20 - Feature not available. For example, s part of the API is not implemented" ); + if( use_ret == -(MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED) ) + mbedtls_snprintf( buf, buflen, "CHACHA20 - Chacha20 hardware accelerator failed" ); +#endif /* MBEDTLS_CHACHA20_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + if( use_ret == -(MBEDTLS_ERR_CHACHAPOLY_BAD_STATE) ) + mbedtls_snprintf( buf, buflen, "CHACHAPOLY - The requested operation is not permitted in the current state" ); + if( use_ret == -(MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED) ) + mbedtls_snprintf( buf, buflen, "CHACHAPOLY - Authenticated decryption failed: data was not authentic" ); +#endif /* MBEDTLS_CHACHAPOLY_C */ + #if defined(MBEDTLS_CMAC_C) if( use_ret == -(MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED) ) mbedtls_snprintf( buf, buflen, "CMAC - CMAC hardware accelerator failed" ); @@ -715,6 +749,11 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "GCM - Bad input parameters to function" ); #endif /* MBEDTLS_GCM_C */ +#if defined(MBEDTLS_HKDF_C) + if( use_ret == -(MBEDTLS_ERR_HKDF_BAD_INPUT_DATA) ) + mbedtls_snprintf( buf, buflen, "HKDF - Bad input parameters to function" ); +#endif /* MBEDTLS_HKDF_C */ + #if defined(MBEDTLS_HMAC_DRBG_C) if( use_ret == -(MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG) ) mbedtls_snprintf( buf, buflen, "HMAC_DRBG - Too many random requested in single call" ); @@ -782,6 +821,15 @@ void mbedtls_strerror( int ret, char *buf, size_t buflen ) mbedtls_snprintf( buf, buflen, "PADLOCK - Input data should be aligned" ); #endif /* MBEDTLS_PADLOCK_C */ +#if defined(MBEDTLS_POLY1305_C) + if( use_ret == -(MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA) ) + mbedtls_snprintf( buf, buflen, "POLY1305 - Invalid input parameter(s)" ); + if( use_ret == -(MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE) ) + mbedtls_snprintf( buf, buflen, "POLY1305 - Feature not available. For example, s part of the API is not implemented" ); + if( use_ret == -(MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED) ) + mbedtls_snprintf( buf, buflen, "POLY1305 - Poly1305 hardware accelerator failed" ); +#endif /* MBEDTLS_POLY1305_C */ + #if defined(MBEDTLS_RIPEMD160_C) if( use_ret == -(MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED) ) mbedtls_snprintf( buf, buflen, "RIPEMD160 - RIPEMD160 hardware accelerator failed" ); diff --git a/thirdparty/mbedtls/library/hkdf.c b/thirdparty/mbedtls/library/hkdf.c new file mode 100644 index 0000000000..82d8a429f4 --- /dev/null +++ b/thirdparty/mbedtls/library/hkdf.c @@ -0,0 +1,192 @@ +/* + * HKDF implementation -- RFC 5869 + * + * Copyright (C) 2016-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_HKDF_C) + +#include <string.h> +#include "mbedtls/hkdf.h" +#include "mbedtls/platform_util.h" + +int mbedtls_hkdf( const mbedtls_md_info_t *md, const unsigned char *salt, + size_t salt_len, const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len ) +{ + int ret; + unsigned char prk[MBEDTLS_MD_MAX_SIZE]; + + ret = mbedtls_hkdf_extract( md, salt, salt_len, ikm, ikm_len, prk ); + + if( ret == 0 ) + { + ret = mbedtls_hkdf_expand( md, prk, mbedtls_md_get_size( md ), + info, info_len, okm, okm_len ); + } + + mbedtls_platform_zeroize( prk, sizeof( prk ) ); + + return( ret ); +} + +int mbedtls_hkdf_extract( const mbedtls_md_info_t *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk ) +{ + unsigned char null_salt[MBEDTLS_MD_MAX_SIZE] = { '\0' }; + + if( salt == NULL ) + { + size_t hash_len; + + if( salt_len != 0 ) + { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + hash_len = mbedtls_md_get_size( md ); + + if( hash_len == 0 ) + { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + salt = null_salt; + salt_len = hash_len; + } + + return( mbedtls_md_hmac( md, salt, salt_len, ikm, ikm_len, prk ) ); +} + +int mbedtls_hkdf_expand( const mbedtls_md_info_t *md, const unsigned char *prk, + size_t prk_len, const unsigned char *info, + size_t info_len, unsigned char *okm, size_t okm_len ) +{ + size_t hash_len; + size_t where = 0; + size_t n; + size_t t_len = 0; + size_t i; + int ret = 0; + mbedtls_md_context_t ctx; + unsigned char t[MBEDTLS_MD_MAX_SIZE]; + + if( okm == NULL ) + { + return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA ); + } + + hash_len = mbedtls_md_get_size( md ); + + if( prk_len < hash_len || hash_len == 0 ) + { + return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA ); + } + + if( info == NULL ) + { + info = (const unsigned char *) ""; + info_len = 0; + } + + n = okm_len / hash_len; + + if( (okm_len % hash_len) != 0 ) + { + n++; + } + + /* + * Per RFC 5869 Section 2.3, okm_len must not exceed + * 255 times the hash length + */ + if( n > 255 ) + { + return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA ); + } + + mbedtls_md_init( &ctx ); + + if( (ret = mbedtls_md_setup( &ctx, md, 1) ) != 0 ) + { + goto exit; + } + + /* + * Compute T = T(1) | T(2) | T(3) | ... | T(N) + * Where T(N) is defined in RFC 5869 Section 2.3 + */ + for( i = 1; i <= n; i++ ) + { + size_t num_to_copy; + unsigned char c = i & 0xff; + + ret = mbedtls_md_hmac_starts( &ctx, prk, prk_len ); + if( ret != 0 ) + { + goto exit; + } + + ret = mbedtls_md_hmac_update( &ctx, t, t_len ); + if( ret != 0 ) + { + goto exit; + } + + ret = mbedtls_md_hmac_update( &ctx, info, info_len ); + if( ret != 0 ) + { + goto exit; + } + + /* The constant concatenated to the end of each T(n) is a single octet. + * */ + ret = mbedtls_md_hmac_update( &ctx, &c, 1 ); + if( ret != 0 ) + { + goto exit; + } + + ret = mbedtls_md_hmac_finish( &ctx, t ); + if( ret != 0 ) + { + goto exit; + } + + num_to_copy = i != n ? hash_len : okm_len - where; + memcpy( okm + where, t, num_to_copy ); + where += hash_len; + t_len = hash_len; + } + +exit: + mbedtls_md_free( &ctx ); + mbedtls_platform_zeroize( t, sizeof( t ) ); + + return( ret ); +} + +#endif /* MBEDTLS_HKDF_C */ diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c index 8238c2b81a..2a740cda81 100644 --- a/thirdparty/mbedtls/library/md5.c +++ b/thirdparty/mbedtls/library/md5.c @@ -309,14 +309,6 @@ void mbedtls_md5_update( mbedtls_md5_context *ctx, } #endif -static const unsigned char md5_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - /* * MD5 final digest */ @@ -324,26 +316,48 @@ int mbedtls_md5_finish_ret( mbedtls_md5_context *ctx, unsigned char output[16] ) { int ret; - uint32_t last, padn; + uint32_t used; uint32_t high, low; - unsigned char msglen[8]; - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; - PUT_UINT32_LE( low, msglen, 0 ); - PUT_UINT32_LE( high, msglen, 4 ); + ctx->buffer[used++] = 0x80; - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + if( used <= 56 ) + { + /* Enough room for padding + length in current block */ + memset( ctx->buffer + used, 0, 56 - used ); + } + else + { + /* We'll need an extra block */ + memset( ctx->buffer + used, 0, 64 - used ); - if( ( ret = mbedtls_md5_update_ret( ctx, md5_padding, padn ) ) != 0 ) + if( ( ret = mbedtls_internal_md5_process( ctx, ctx->buffer ) ) != 0 ) return( ret ); - if( ( ret = mbedtls_md5_update_ret( ctx, msglen, 8 ) ) != 0 ) - return( ret ); + memset( ctx->buffer, 0, 56 ); + } + + /* + * Add message length + */ + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, ctx->buffer, 56 ); + PUT_UINT32_LE( high, ctx->buffer, 60 ); + + if( ( ret = mbedtls_internal_md5_process( ctx, ctx->buffer ) ) != 0 ) + return( ret ); + /* + * Output final state + */ PUT_UINT32_LE( ctx->state[0], output, 0 ); PUT_UINT32_LE( ctx->state[1], output, 4 ); PUT_UINT32_LE( ctx->state[2], output, 8 ); diff --git a/thirdparty/mbedtls/library/memory_buffer_alloc.c b/thirdparty/mbedtls/library/memory_buffer_alloc.c index ceaeda1e73..51ea7c41d7 100644 --- a/thirdparty/mbedtls/library/memory_buffer_alloc.c +++ b/thirdparty/mbedtls/library/memory_buffer_alloc.c @@ -518,7 +518,9 @@ void mbedtls_memory_buffer_alloc_status( void ) heap.alloc_count, heap.free_count ); if( heap.first->next == NULL ) + { mbedtls_fprintf( stderr, "All memory de-allocated in stack buffer\n" ); + } else { mbedtls_fprintf( stderr, "Memory currently allocated:\n" ); diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c index 9b0a375fb7..816b1303df 100644 --- a/thirdparty/mbedtls/library/net_sockets.c +++ b/thirdparty/mbedtls/library/net_sockets.c @@ -19,6 +19,11 @@ * This file is part of mbed TLS (https://tls.mbed.org) */ +/* Enable definition of getaddrinfo() even when compiling with -std=c99. Must + * be set before config.h, which pulls in glibc's features.h indirectly. + * Harmless on other platforms. */ +#define _POSIX_C_SOURCE 200112L + #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else @@ -28,7 +33,8 @@ #if defined(MBEDTLS_NET_C) #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ - !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) #error "This module only works on Unix and Windows, see MBEDTLS_NET_C in config.h" #endif @@ -47,13 +53,11 @@ #define IS_EINTR( ret ) ( ( ret ) == WSAEINTR ) -/* GODOT ADDITION */ #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501) #undef _WIN32_WINNT /* Enables getaddrinfo() & Co */ #define _WIN32_WINNT 0x0501 #endif -/* END GODOT ADDITION */ #include <ws2tcpip.h> diff --git a/thirdparty/mbedtls/library/nist_kw.c b/thirdparty/mbedtls/library/nist_kw.c new file mode 100644 index 0000000000..176af9fe08 --- /dev/null +++ b/thirdparty/mbedtls/library/nist_kw.c @@ -0,0 +1,755 @@ +/* + * Implementation of NIST SP 800-38F key wrapping, supporting KW and KWP modes + * only + * + * Copyright (C) 2018, Arm Limited (or its affiliates), All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ +/* + * Definition of Key Wrapping: + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf + * RFC 3394 "Advanced Encryption Standard (AES) Key Wrap Algorithm" + * RFC 5649 "Advanced Encryption Standard (AES) Key Wrap with Padding Algorithm" + * + * Note: RFC 3394 defines different methodology for intermediate operations for + * the wrapping and unwrapping operation than the definition in NIST SP 800-38F. + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_NIST_KW_C) + +#include "mbedtls/nist_kw.h" +#include "mbedtls/platform_util.h" + +#include <stdint.h> +#include <string.h> + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include <stdio.h> +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_C */ +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#if !defined(MBEDTLS_NIST_KW_ALT) + +#define KW_SEMIBLOCK_LENGTH 8 +#define MIN_SEMIBLOCKS_COUNT 3 + +/* constant-time buffer comparison */ +static inline unsigned char mbedtls_nist_kw_safer_memcmp( const void *a, const void *b, size_t n ) +{ + size_t i; + volatile const unsigned char *A = (volatile const unsigned char *) a; + volatile const unsigned char *B = (volatile const unsigned char *) b; + volatile unsigned char diff = 0; + + for( i = 0; i < n; i++ ) + { + /* Read volatile data in order before computing diff. + * This avoids IAR compiler warning: + * 'the order of volatile accesses is undefined ..' */ + unsigned char x = A[i], y = B[i]; + diff |= x ^ y; + } + + return( diff ); +} + +/*! The 64-bit default integrity check value (ICV) for KW mode. */ +static const unsigned char NIST_KW_ICV1[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; +/*! The 32-bit default integrity check value (ICV) for KWP mode. */ +static const unsigned char NIST_KW_ICV2[] = {0xA6, 0x59, 0x59, 0xA6}; + +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +do { \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} while( 0 ) +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +do { \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} while( 0 ) +#endif + +/* + * Initialize context + */ +void mbedtls_nist_kw_init( mbedtls_nist_kw_context *ctx ) +{ + memset( ctx, 0, sizeof( mbedtls_nist_kw_context ) ); +} + +int mbedtls_nist_kw_setkey( mbedtls_nist_kw_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits, + const int is_wrap ) +{ + int ret; + const mbedtls_cipher_info_t *cipher_info; + + cipher_info = mbedtls_cipher_info_from_values( cipher, + keybits, + MBEDTLS_MODE_ECB ); + if( cipher_info == NULL ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + if( cipher_info->block_size != 16 ) + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + + /* + * SP 800-38F currently defines AES cipher as the only block cipher allowed: + * "For KW and KWP, the underlying block cipher shall be approved, and the + * block size shall be 128 bits. Currently, the AES block cipher, with key + * lengths of 128, 192, or 256 bits, is the only block cipher that fits + * this profile." + * Currently we don't support other 128 bit block ciphers for key wrapping, + * such as Camellia and Aria. + */ + if( cipher != MBEDTLS_CIPHER_ID_AES ) + return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ); + + mbedtls_cipher_free( &ctx->cipher_ctx ); + + if( ( ret = mbedtls_cipher_setup( &ctx->cipher_ctx, cipher_info ) ) != 0 ) + return( ret ); + + if( ( ret = mbedtls_cipher_setkey( &ctx->cipher_ctx, key, keybits, + is_wrap ? MBEDTLS_ENCRYPT : + MBEDTLS_DECRYPT ) + ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} + +/* + * Free context + */ +void mbedtls_nist_kw_free( mbedtls_nist_kw_context *ctx ) +{ + mbedtls_cipher_free( &ctx->cipher_ctx ); + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_nist_kw_context ) ); +} + +/* + * Helper function for Xoring the uint64_t "t" with the encrypted A. + * Defined in NIST SP 800-38F section 6.1 + */ +static void calc_a_xor_t( unsigned char A[KW_SEMIBLOCK_LENGTH], uint64_t t ) +{ + size_t i = 0; + for( i = 0; i < sizeof( t ); i++ ) + { + A[i] ^= ( t >> ( ( sizeof( t ) - 1 - i ) * 8 ) ) & 0xff; + } +} + +/* + * KW-AE as defined in SP 800-38F section 6.2 + * KWP-AE as defined in SP 800-38F section 6.3 + */ +int mbedtls_nist_kw_wrap( mbedtls_nist_kw_context *ctx, + mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size ) +{ + int ret = 0; + size_t semiblocks = 0; + size_t s; + size_t olen, padlen = 0; + uint64_t t = 0; + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char inbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char *R2 = output + KW_SEMIBLOCK_LENGTH; + unsigned char *A = output; + + *out_len = 0; + /* + * Generate the String to work on + */ + if( mode == MBEDTLS_KW_MODE_KW ) + { + if( out_size < in_len + KW_SEMIBLOCK_LENGTH ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + /* + * According to SP 800-38F Table 1, the plaintext length for KW + * must be between 2 to 2^54-1 semiblocks inclusive. + */ + if( in_len < 16 || +#if SIZE_MAX > 0x1FFFFFFFFFFFFF8 + in_len > 0x1FFFFFFFFFFFFF8 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0 ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + memcpy( output, NIST_KW_ICV1, KW_SEMIBLOCK_LENGTH ); + memmove( output + KW_SEMIBLOCK_LENGTH, input, in_len ); + } + else + { + if( in_len % 8 != 0 ) + { + padlen = ( 8 - ( in_len % 8 ) ); + } + + if( out_size < in_len + KW_SEMIBLOCK_LENGTH + padlen ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + /* + * According to SP 800-38F Table 1, the plaintext length for KWP + * must be between 1 and 2^32-1 octets inclusive. + */ + if( in_len < 1 +#if SIZE_MAX > 0xFFFFFFFF + || in_len > 0xFFFFFFFF +#endif + ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + memcpy( output, NIST_KW_ICV2, KW_SEMIBLOCK_LENGTH / 2 ); + PUT_UINT32_BE( ( in_len & 0xffffffff ), output, + KW_SEMIBLOCK_LENGTH / 2 ); + + memcpy( output + KW_SEMIBLOCK_LENGTH, input, in_len ); + memset( output + KW_SEMIBLOCK_LENGTH + in_len, 0, padlen ); + } + semiblocks = ( ( in_len + padlen ) / KW_SEMIBLOCK_LENGTH ) + 1; + + s = 6 * ( semiblocks - 1 ); + + if( mode == MBEDTLS_KW_MODE_KWP + && in_len <= KW_SEMIBLOCK_LENGTH ) + { + memcpy( inbuff, output, 16 ); + ret = mbedtls_cipher_update( &ctx->cipher_ctx, + inbuff, 16, output, &olen ); + if( ret != 0 ) + goto cleanup; + } + else + { + /* + * Do the wrapping function W, as defined in RFC 3394 section 2.2.1 + */ + if( semiblocks < MIN_SEMIBLOCKS_COUNT ) + { + ret = MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + goto cleanup; + } + + /* Calculate intermediate values */ + for( t = 1; t <= s; t++ ) + { + memcpy( inbuff, A, KW_SEMIBLOCK_LENGTH ); + memcpy( inbuff + KW_SEMIBLOCK_LENGTH, R2, KW_SEMIBLOCK_LENGTH ); + + ret = mbedtls_cipher_update( &ctx->cipher_ctx, + inbuff, 16, outbuff, &olen ); + if( ret != 0 ) + goto cleanup; + + memcpy( A, outbuff, KW_SEMIBLOCK_LENGTH ); + calc_a_xor_t( A, t ); + + memcpy( R2, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH ); + R2 += KW_SEMIBLOCK_LENGTH; + if( R2 >= output + ( semiblocks * KW_SEMIBLOCK_LENGTH ) ) + R2 = output + KW_SEMIBLOCK_LENGTH; + } + } + + *out_len = semiblocks * KW_SEMIBLOCK_LENGTH; + +cleanup: + + if( ret != 0) + { + memset( output, 0, semiblocks * KW_SEMIBLOCK_LENGTH ); + } + mbedtls_platform_zeroize( inbuff, KW_SEMIBLOCK_LENGTH * 2 ); + mbedtls_platform_zeroize( outbuff, KW_SEMIBLOCK_LENGTH * 2 ); + mbedtls_cipher_finish( &ctx->cipher_ctx, NULL, &olen ); + return( ret ); +} + +/* + * W-1 function as defined in RFC 3394 section 2.2.2 + * This function assumes the following: + * 1. Output buffer is at least of size ( semiblocks - 1 ) * KW_SEMIBLOCK_LENGTH. + * 2. The input buffer is of size semiblocks * KW_SEMIBLOCK_LENGTH. + * 3. Minimal number of semiblocks is 3. + * 4. A is a buffer to hold the first semiblock of the input buffer. + */ +static int unwrap( mbedtls_nist_kw_context *ctx, + const unsigned char *input, size_t semiblocks, + unsigned char A[KW_SEMIBLOCK_LENGTH], + unsigned char *output, size_t* out_len ) +{ + int ret = 0; + const size_t s = 6 * ( semiblocks - 1 ); + size_t olen; + uint64_t t = 0; + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char inbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char *R = output + ( semiblocks - 2 ) * KW_SEMIBLOCK_LENGTH; + *out_len = 0; + + if( semiblocks < MIN_SEMIBLOCKS_COUNT ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + memcpy( A, input, KW_SEMIBLOCK_LENGTH ); + memmove( output, input + KW_SEMIBLOCK_LENGTH, ( semiblocks - 1 ) * KW_SEMIBLOCK_LENGTH ); + + /* Calculate intermediate values */ + for( t = s; t >= 1; t-- ) + { + calc_a_xor_t( A, t ); + + memcpy( inbuff, A, KW_SEMIBLOCK_LENGTH ); + memcpy( inbuff + KW_SEMIBLOCK_LENGTH, R, KW_SEMIBLOCK_LENGTH ); + + ret = mbedtls_cipher_update( &ctx->cipher_ctx, + inbuff, 16, outbuff, &olen ); + if( ret != 0 ) + goto cleanup; + + memcpy( A, outbuff, KW_SEMIBLOCK_LENGTH ); + + /* Set R as LSB64 of outbuff */ + memcpy( R, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH ); + + if( R == output ) + R = output + ( semiblocks - 2 ) * KW_SEMIBLOCK_LENGTH; + else + R -= KW_SEMIBLOCK_LENGTH; + } + + *out_len = ( semiblocks - 1 ) * KW_SEMIBLOCK_LENGTH; + +cleanup: + if( ret != 0) + memset( output, 0, ( semiblocks - 1 ) * KW_SEMIBLOCK_LENGTH ); + mbedtls_platform_zeroize( inbuff, sizeof( inbuff ) ); + mbedtls_platform_zeroize( outbuff, sizeof( outbuff ) ); + + return( ret ); +} + +/* + * KW-AD as defined in SP 800-38F section 6.2 + * KWP-AD as defined in SP 800-38F section 6.3 + */ +int mbedtls_nist_kw_unwrap( mbedtls_nist_kw_context *ctx, + mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size ) +{ + int ret = 0; + size_t i, olen; + unsigned char A[KW_SEMIBLOCK_LENGTH]; + unsigned char diff, bad_padding = 0; + + *out_len = 0; + if( out_size < in_len - KW_SEMIBLOCK_LENGTH ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + if( mode == MBEDTLS_KW_MODE_KW ) + { + /* + * According to SP 800-38F Table 1, the ciphertext length for KW + * must be between 3 to 2^54 semiblocks inclusive. + */ + if( in_len < 24 || +#if SIZE_MAX > 0x200000000000000 + in_len > 0x200000000000000 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0 ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + ret = unwrap( ctx, input, in_len / KW_SEMIBLOCK_LENGTH, + A, output, out_len ); + if( ret != 0 ) + goto cleanup; + + /* Check ICV in "constant-time" */ + diff = mbedtls_nist_kw_safer_memcmp( NIST_KW_ICV1, A, KW_SEMIBLOCK_LENGTH ); + + if( diff != 0 ) + { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + goto cleanup; + } + + } + else if( mode == MBEDTLS_KW_MODE_KWP ) + { + size_t padlen = 0; + uint32_t Plen; + /* + * According to SP 800-38F Table 1, the ciphertext length for KWP + * must be between 2 to 2^29 semiblocks inclusive. + */ + if( in_len < KW_SEMIBLOCK_LENGTH * 2 || +#if SIZE_MAX > 0x100000000 + in_len > 0x100000000 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0 ) + { + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + if( in_len == KW_SEMIBLOCK_LENGTH * 2 ) + { + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + ret = mbedtls_cipher_update( &ctx->cipher_ctx, + input, 16, outbuff, &olen ); + if( ret != 0 ) + goto cleanup; + + memcpy( A, outbuff, KW_SEMIBLOCK_LENGTH ); + memcpy( output, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH ); + mbedtls_platform_zeroize( outbuff, sizeof( outbuff ) ); + *out_len = KW_SEMIBLOCK_LENGTH; + } + else + { + /* in_len >= KW_SEMIBLOCK_LENGTH * 3 */ + ret = unwrap( ctx, input, in_len / KW_SEMIBLOCK_LENGTH, + A, output, out_len ); + if( ret != 0 ) + goto cleanup; + } + + /* Check ICV in "constant-time" */ + diff = mbedtls_nist_kw_safer_memcmp( NIST_KW_ICV2, A, KW_SEMIBLOCK_LENGTH / 2 ); + + if( diff != 0 ) + { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + GET_UINT32_BE( Plen, A, KW_SEMIBLOCK_LENGTH / 2 ); + + /* + * Plen is the length of the plaintext, when the input is valid. + * If Plen is larger than the plaintext and padding, padlen will be + * larger than 8, because of the type wrap around. + */ + padlen = in_len - KW_SEMIBLOCK_LENGTH - Plen; + if ( padlen > 7 ) + { + padlen &= 7; + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + /* Check padding in "constant-time" */ + for( diff = 0, i = 0; i < KW_SEMIBLOCK_LENGTH; i++ ) + { + if( i >= KW_SEMIBLOCK_LENGTH - padlen ) + diff |= output[*out_len - KW_SEMIBLOCK_LENGTH + i]; + else + bad_padding |= output[*out_len - KW_SEMIBLOCK_LENGTH + i]; + } + + if( diff != 0 ) + { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + if( ret != 0 ) + { + goto cleanup; + } + memset( output + Plen, 0, padlen ); + *out_len = Plen; + } + else + { + ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + goto cleanup; + } + +cleanup: + if( ret != 0 ) + { + memset( output, 0, *out_len ); + *out_len = 0; + } + + mbedtls_platform_zeroize( &bad_padding, sizeof( bad_padding) ); + mbedtls_platform_zeroize( &diff, sizeof( diff ) ); + mbedtls_platform_zeroize( A, sizeof( A ) ); + mbedtls_cipher_finish( &ctx->cipher_ctx, NULL, &olen ); + return( ret ); +} + +#endif /* !MBEDTLS_NIST_KW_ALT */ + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) + +#define KW_TESTS 3 + +/* + * Test vectors taken from NIST + * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#KW + */ +static const unsigned int key_len[KW_TESTS] = { 16, 24, 32 }; + +static const unsigned char kw_key[KW_TESTS][32] = { + { 0x75, 0x75, 0xda, 0x3a, 0x93, 0x60, 0x7c, 0xc2, + 0xbf, 0xd8, 0xce, 0xc7, 0xaa, 0xdf, 0xd9, 0xa6 }, + { 0x2d, 0x85, 0x26, 0x08, 0x1d, 0x02, 0xfb, 0x5b, + 0x85, 0xf6, 0x9a, 0xc2, 0x86, 0xec, 0xd5, 0x7d, + 0x40, 0xdf, 0x5d, 0xf3, 0x49, 0x47, 0x44, 0xd3 }, + { 0x11, 0x2a, 0xd4, 0x1b, 0x48, 0x56, 0xc7, 0x25, + 0x4a, 0x98, 0x48, 0xd3, 0x0f, 0xdd, 0x78, 0x33, + 0x5b, 0x03, 0x9a, 0x48, 0xa8, 0x96, 0x2c, 0x4d, + 0x1c, 0xb7, 0x8e, 0xab, 0xd5, 0xda, 0xd7, 0x88 } +}; + +static const unsigned char kw_msg[KW_TESTS][40] = { + { 0x42, 0x13, 0x6d, 0x3c, 0x38, 0x4a, 0x3e, 0xea, + 0xc9, 0x5a, 0x06, 0x6f, 0xd2, 0x8f, 0xed, 0x3f }, + { 0x95, 0xc1, 0x1b, 0xf5, 0x35, 0x3a, 0xfe, 0xdb, + 0x98, 0xfd, 0xd6, 0xc8, 0xca, 0x6f, 0xdb, 0x6d, + 0xa5, 0x4b, 0x74, 0xb4, 0x99, 0x0f, 0xdc, 0x45, + 0xc0, 0x9d, 0x15, 0x8f, 0x51, 0xce, 0x62, 0x9d, + 0xe2, 0xaf, 0x26, 0xe3, 0x25, 0x0e, 0x6b, 0x4c }, + { 0x1b, 0x20, 0xbf, 0x19, 0x90, 0xb0, 0x65, 0xd7, + 0x98, 0xe1, 0xb3, 0x22, 0x64, 0xad, 0x50, 0xa8, + 0x74, 0x74, 0x92, 0xba, 0x09, 0xa0, 0x4d, 0xd1 } +}; + +static const size_t kw_msg_len[KW_TESTS] = { 16, 40, 24 }; +static const size_t kw_out_len[KW_TESTS] = { 24, 48, 32 }; +static const unsigned char kw_res[KW_TESTS][48] = { + { 0x03, 0x1f, 0x6b, 0xd7, 0xe6, 0x1e, 0x64, 0x3d, + 0xf6, 0x85, 0x94, 0x81, 0x6f, 0x64, 0xca, 0xa3, + 0xf5, 0x6f, 0xab, 0xea, 0x25, 0x48, 0xf5, 0xfb }, + { 0x44, 0x3c, 0x6f, 0x15, 0x09, 0x83, 0x71, 0x91, + 0x3e, 0x5c, 0x81, 0x4c, 0xa1, 0xa0, 0x42, 0xec, + 0x68, 0x2f, 0x7b, 0x13, 0x6d, 0x24, 0x3a, 0x4d, + 0x6c, 0x42, 0x6f, 0xc6, 0x97, 0x15, 0x63, 0xe8, + 0xa1, 0x4a, 0x55, 0x8e, 0x09, 0x64, 0x16, 0x19, + 0xbf, 0x03, 0xfc, 0xaf, 0x90, 0xb1, 0xfc, 0x2d }, + { 0xba, 0x8a, 0x25, 0x9a, 0x47, 0x1b, 0x78, 0x7d, + 0xd5, 0xd5, 0x40, 0xec, 0x25, 0xd4, 0x3d, 0x87, + 0x20, 0x0f, 0xda, 0xdc, 0x6d, 0x1f, 0x05, 0xd9, + 0x16, 0x58, 0x4f, 0xa9, 0xf6, 0xcb, 0xf5, 0x12 } +}; + +static const unsigned char kwp_key[KW_TESTS][32] = { + { 0x78, 0x65, 0xe2, 0x0f, 0x3c, 0x21, 0x65, 0x9a, + 0xb4, 0x69, 0x0b, 0x62, 0x9c, 0xdf, 0x3c, 0xc4 }, + { 0xf5, 0xf8, 0x96, 0xa3, 0xbd, 0x2f, 0x4a, 0x98, + 0x23, 0xef, 0x16, 0x2b, 0x00, 0xb8, 0x05, 0xd7, + 0xde, 0x1e, 0xa4, 0x66, 0x26, 0x96, 0xa2, 0x58 }, + { 0x95, 0xda, 0x27, 0x00, 0xca, 0x6f, 0xd9, 0xa5, + 0x25, 0x54, 0xee, 0x2a, 0x8d, 0xf1, 0x38, 0x6f, + 0x5b, 0x94, 0xa1, 0xa6, 0x0e, 0xd8, 0xa4, 0xae, + 0xf6, 0x0a, 0x8d, 0x61, 0xab, 0x5f, 0x22, 0x5a } +}; + +static const unsigned char kwp_msg[KW_TESTS][31] = { + { 0xbd, 0x68, 0x43, 0xd4, 0x20, 0x37, 0x8d, 0xc8, + 0x96 }, + { 0x6c, 0xcd, 0xd5, 0x85, 0x18, 0x40, 0x97, 0xeb, + 0xd5, 0xc3, 0xaf, 0x3e, 0x47, 0xd0, 0x2c, 0x19, + 0x14, 0x7b, 0x4d, 0x99, 0x5f, 0x96, 0x43, 0x66, + 0x91, 0x56, 0x75, 0x8c, 0x13, 0x16, 0x8f }, + { 0xd1 } +}; +static const size_t kwp_msg_len[KW_TESTS] = { 9, 31, 1 }; + +static const unsigned char kwp_res[KW_TESTS][48] = { + { 0x41, 0xec, 0xa9, 0x56, 0xd4, 0xaa, 0x04, 0x7e, + 0xb5, 0xcf, 0x4e, 0xfe, 0x65, 0x96, 0x61, 0xe7, + 0x4d, 0xb6, 0xf8, 0xc5, 0x64, 0xe2, 0x35, 0x00 }, + { 0x4e, 0x9b, 0xc2, 0xbc, 0xbc, 0x6c, 0x1e, 0x13, + 0xd3, 0x35, 0xbc, 0xc0, 0xf7, 0x73, 0x6a, 0x88, + 0xfa, 0x87, 0x53, 0x66, 0x15, 0xbb, 0x8e, 0x63, + 0x8b, 0xcc, 0x81, 0x66, 0x84, 0x68, 0x17, 0x90, + 0x67, 0xcf, 0xa9, 0x8a, 0x9d, 0x0e, 0x33, 0x26 }, + { 0x06, 0xba, 0x7a, 0xe6, 0xf3, 0x24, 0x8c, 0xfd, + 0xcf, 0x26, 0x75, 0x07, 0xfa, 0x00, 0x1b, 0xc4 } +}; +static const size_t kwp_out_len[KW_TESTS] = { 24, 40, 16 }; + +int mbedtls_nist_kw_self_test( int verbose ) +{ + mbedtls_nist_kw_context ctx; + unsigned char out[48]; + size_t olen; + int i; + int ret = 0; + mbedtls_nist_kw_init( &ctx ); + + for( i = 0; i < KW_TESTS; i++ ) + { + if( verbose != 0 ) + mbedtls_printf( " KW-AES-%u ", (unsigned int) key_len[i] * 8 ); + + ret = mbedtls_nist_kw_setkey( &ctx, MBEDTLS_CIPHER_ID_AES, + kw_key[i], key_len[i] * 8, 1 ); + if( ret != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( " KW: setup failed " ); + + goto end; + } + + ret = mbedtls_nist_kw_wrap( &ctx, MBEDTLS_KW_MODE_KW, kw_msg[i], + kw_msg_len[i], out, &olen, sizeof( out ) ); + if( ret != 0 || kw_out_len[i] != olen || + memcmp( out, kw_res[i], kw_out_len[i] ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed. "); + + ret = 1; + goto end; + } + + if( ( ret = mbedtls_nist_kw_setkey( &ctx, MBEDTLS_CIPHER_ID_AES, + kw_key[i], key_len[i] * 8, 0 ) ) + != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( " KW: setup failed "); + + goto end; + } + + ret = mbedtls_nist_kw_unwrap( &ctx, MBEDTLS_KW_MODE_KW, + out, olen, out, &olen, sizeof( out ) ); + + if( ret != 0 || olen != kw_msg_len[i] || + memcmp( out, kw_msg[i], kw_msg_len[i] ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed\n" ); + + ret = 1; + goto end; + } + + if( verbose != 0 ) + mbedtls_printf( " passed\n" ); + } + + for( i = 0; i < KW_TESTS; i++ ) + { + olen = sizeof( out ); + if( verbose != 0 ) + mbedtls_printf( " KWP-AES-%u ", (unsigned int) key_len[i] * 8 ); + + ret = mbedtls_nist_kw_setkey( &ctx, MBEDTLS_CIPHER_ID_AES, kwp_key[i], + key_len[i] * 8, 1 ); + if( ret != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( " KWP: setup failed " ); + + goto end; + } + ret = mbedtls_nist_kw_wrap( &ctx, MBEDTLS_KW_MODE_KWP, kwp_msg[i], + kwp_msg_len[i], out, &olen, sizeof( out ) ); + + if( ret != 0 || kwp_out_len[i] != olen || + memcmp( out, kwp_res[i], kwp_out_len[i] ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed. "); + + ret = 1; + goto end; + } + + if( ( ret = mbedtls_nist_kw_setkey( &ctx, MBEDTLS_CIPHER_ID_AES, + kwp_key[i], key_len[i] * 8, 0 ) ) + != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( " KWP: setup failed "); + + goto end; + } + + ret = mbedtls_nist_kw_unwrap( &ctx, MBEDTLS_KW_MODE_KWP, out, + olen, out, &olen, sizeof( out ) ); + + if( ret != 0 || olen != kwp_msg_len[i] || + memcmp( out, kwp_msg[i], kwp_msg_len[i] ) != 0 ) + { + if( verbose != 0 ) + mbedtls_printf( "failed. "); + + ret = 1; + goto end; + } + + if( verbose != 0 ) + mbedtls_printf( " passed\n" ); + } +end: + mbedtls_nist_kw_free( &ctx ); + + if( verbose != 0 ) + mbedtls_printf( "\n" ); + + return( ret ); +} + +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#endif /* MBEDTLS_NIST_KW_C */ diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c index 440a174b5b..f04f0ab25e 100644 --- a/thirdparty/mbedtls/library/pkcs5.c +++ b/thirdparty/mbedtls/library/pkcs5.c @@ -249,8 +249,10 @@ int mbedtls_pkcs5_pbkdf2_hmac( mbedtls_md_context_t *ctx, const unsigned char *p memset( counter, 0, 4 ); counter[3] = 1; +#if UINT_MAX > 0xFFFFFFFF if( iteration_count > 0xFFFFFFFF ) return( MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA ); +#endif while( key_length ) { diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c index ccb7f5409d..d6ac987e23 100644 --- a/thirdparty/mbedtls/library/pkparse.c +++ b/thirdparty/mbedtls/library/pkparse.c @@ -1261,7 +1261,6 @@ int mbedtls_pk_parse_key( mbedtls_pk_context *pk, return( ret ); #endif /* MBEDTLS_PKCS12_C || MBEDTLS_PKCS5_C */ #else - ((void) ret); ((void) pwd); ((void) pwdlen); #endif /* MBEDTLS_PEM_PARSE_C */ diff --git a/thirdparty/mbedtls/library/platform.c b/thirdparty/mbedtls/library/platform.c index 9e992875d9..b24b2fa652 100644 --- a/thirdparty/mbedtls/library/platform.c +++ b/thirdparty/mbedtls/library/platform.c @@ -51,14 +51,24 @@ static void platform_free_uninit( void *ptr ) #define MBEDTLS_PLATFORM_STD_FREE platform_free_uninit #endif /* !MBEDTLS_PLATFORM_STD_FREE */ -void * (*mbedtls_calloc)( size_t, size_t ) = MBEDTLS_PLATFORM_STD_CALLOC; -void (*mbedtls_free)( void * ) = MBEDTLS_PLATFORM_STD_FREE; +static void * (*mbedtls_calloc_func)( size_t, size_t ) = MBEDTLS_PLATFORM_STD_CALLOC; +static void (*mbedtls_free_func)( void * ) = MBEDTLS_PLATFORM_STD_FREE; + +void * mbedtls_calloc( size_t nmemb, size_t size ) +{ + return (*mbedtls_calloc_func)( nmemb, size ); +} + +void mbedtls_free( void * ptr ) +{ + (*mbedtls_free_func)( ptr ); +} int mbedtls_platform_set_calloc_free( void * (*calloc_func)( size_t, size_t ), void (*free_func)( void * ) ) { - mbedtls_calloc = calloc_func; - mbedtls_free = free_func; + mbedtls_calloc_func = calloc_func; + mbedtls_free_func = free_func; return( 0 ); } #endif /* MBEDTLS_PLATFORM_MEMORY */ diff --git a/thirdparty/mbedtls/library/poly1305.c b/thirdparty/mbedtls/library/poly1305.c new file mode 100644 index 0000000000..e22d3afb68 --- /dev/null +++ b/thirdparty/mbedtls/library/poly1305.c @@ -0,0 +1,563 @@ +/** + * \file poly1305.c + * + * \brief Poly1305 authentication algorithm. + * + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_POLY1305_C) + +#include "mbedtls/poly1305.h" +#include "mbedtls/platform_util.h" + +#include <string.h> + +#if defined(MBEDTLS_SELF_TEST) +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include <stdio.h> +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_C */ +#endif /* MBEDTLS_SELF_TEST */ + +#if !defined(MBEDTLS_POLY1305_ALT) + +#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +#define POLY1305_BLOCK_SIZE_BYTES ( 16U ) + +#define BYTES_TO_U32_LE( data, offset ) \ + ( (uint32_t) data[offset] \ + | (uint32_t) ( (uint32_t) data[( offset ) + 1] << 8 ) \ + | (uint32_t) ( (uint32_t) data[( offset ) + 2] << 16 ) \ + | (uint32_t) ( (uint32_t) data[( offset ) + 3] << 24 ) \ + ) + +/* + * Our implementation is tuned for 32-bit platforms with a 64-bit multiplier. + * However we provided an alternative for platforms without such a multiplier. + */ +#if defined(MBEDTLS_NO_64BIT_MULTIPLICATION) +static uint64_t mul64( uint32_t a, uint32_t b ) +{ + /* a = al + 2**16 ah, b = bl + 2**16 bh */ + const uint16_t al = (uint16_t) a; + const uint16_t bl = (uint16_t) b; + const uint16_t ah = a >> 16; + const uint16_t bh = b >> 16; + + /* ab = al*bl + 2**16 (ah*bl + bl*bh) + 2**32 ah*bh */ + const uint32_t lo = (uint32_t) al * bl; + const uint64_t me = (uint64_t)( (uint32_t) ah * bl ) + (uint32_t) al * bh; + const uint32_t hi = (uint32_t) ah * bh; + + return( lo + ( me << 16 ) + ( (uint64_t) hi << 32 ) ); +} +#else +static inline uint64_t mul64( uint32_t a, uint32_t b ) +{ + return( (uint64_t) a * b ); +} +#endif + + +/** + * \brief Process blocks with Poly1305. + * + * \param ctx The Poly1305 context. + * \param nblocks Number of blocks to process. Note that this + * function only processes full blocks. + * \param input Buffer containing the input block(s). + * \param needs_padding Set to 0 if the padding bit has already been + * applied to the input data before calling this + * function. Otherwise, set this parameter to 1. + */ +static void poly1305_process( mbedtls_poly1305_context *ctx, + size_t nblocks, + const unsigned char *input, + uint32_t needs_padding ) +{ + uint64_t d0, d1, d2, d3; + uint32_t acc0, acc1, acc2, acc3, acc4; + uint32_t r0, r1, r2, r3; + uint32_t rs1, rs2, rs3; + size_t offset = 0U; + size_t i; + + r0 = ctx->r[0]; + r1 = ctx->r[1]; + r2 = ctx->r[2]; + r3 = ctx->r[3]; + + rs1 = r1 + ( r1 >> 2U ); + rs2 = r2 + ( r2 >> 2U ); + rs3 = r3 + ( r3 >> 2U ); + + acc0 = ctx->acc[0]; + acc1 = ctx->acc[1]; + acc2 = ctx->acc[2]; + acc3 = ctx->acc[3]; + acc4 = ctx->acc[4]; + + /* Process full blocks */ + for( i = 0U; i < nblocks; i++ ) + { + /* The input block is treated as a 128-bit little-endian integer */ + d0 = BYTES_TO_U32_LE( input, offset + 0 ); + d1 = BYTES_TO_U32_LE( input, offset + 4 ); + d2 = BYTES_TO_U32_LE( input, offset + 8 ); + d3 = BYTES_TO_U32_LE( input, offset + 12 ); + + /* Compute: acc += (padded) block as a 130-bit integer */ + d0 += (uint64_t) acc0; + d1 += (uint64_t) acc1 + ( d0 >> 32U ); + d2 += (uint64_t) acc2 + ( d1 >> 32U ); + d3 += (uint64_t) acc3 + ( d2 >> 32U ); + acc0 = (uint32_t) d0; + acc1 = (uint32_t) d1; + acc2 = (uint32_t) d2; + acc3 = (uint32_t) d3; + acc4 += (uint32_t) ( d3 >> 32U ) + needs_padding; + + /* Compute: acc *= r */ + d0 = mul64( acc0, r0 ) + + mul64( acc1, rs3 ) + + mul64( acc2, rs2 ) + + mul64( acc3, rs1 ); + d1 = mul64( acc0, r1 ) + + mul64( acc1, r0 ) + + mul64( acc2, rs3 ) + + mul64( acc3, rs2 ) + + mul64( acc4, rs1 ); + d2 = mul64( acc0, r2 ) + + mul64( acc1, r1 ) + + mul64( acc2, r0 ) + + mul64( acc3, rs3 ) + + mul64( acc4, rs2 ); + d3 = mul64( acc0, r3 ) + + mul64( acc1, r2 ) + + mul64( acc2, r1 ) + + mul64( acc3, r0 ) + + mul64( acc4, rs3 ); + acc4 *= r0; + + /* Compute: acc %= (2^130 - 5) (partial remainder) */ + d1 += ( d0 >> 32 ); + d2 += ( d1 >> 32 ); + d3 += ( d2 >> 32 ); + acc0 = (uint32_t) d0; + acc1 = (uint32_t) d1; + acc2 = (uint32_t) d2; + acc3 = (uint32_t) d3; + acc4 = (uint32_t) ( d3 >> 32 ) + acc4; + + d0 = (uint64_t) acc0 + ( acc4 >> 2 ) + ( acc4 & 0xFFFFFFFCU ); + acc4 &= 3U; + acc0 = (uint32_t) d0; + d0 = (uint64_t) acc1 + ( d0 >> 32U ); + acc1 = (uint32_t) d0; + d0 = (uint64_t) acc2 + ( d0 >> 32U ); + acc2 = (uint32_t) d0; + d0 = (uint64_t) acc3 + ( d0 >> 32U ); + acc3 = (uint32_t) d0; + d0 = (uint64_t) acc4 + ( d0 >> 32U ); + acc4 = (uint32_t) d0; + + offset += POLY1305_BLOCK_SIZE_BYTES; + } + + ctx->acc[0] = acc0; + ctx->acc[1] = acc1; + ctx->acc[2] = acc2; + ctx->acc[3] = acc3; + ctx->acc[4] = acc4; +} + +/** + * \brief Compute the Poly1305 MAC + * + * \param ctx The Poly1305 context. + * \param mac The buffer to where the MAC is written. Must be + * big enough to contain the 16-byte MAC. + */ +static void poly1305_compute_mac( const mbedtls_poly1305_context *ctx, + unsigned char mac[16] ) +{ + uint64_t d; + uint32_t g0, g1, g2, g3, g4; + uint32_t acc0, acc1, acc2, acc3, acc4; + uint32_t mask; + uint32_t mask_inv; + + acc0 = ctx->acc[0]; + acc1 = ctx->acc[1]; + acc2 = ctx->acc[2]; + acc3 = ctx->acc[3]; + acc4 = ctx->acc[4]; + + /* Before adding 's' we ensure that the accumulator is mod 2^130 - 5. + * We do this by calculating acc - (2^130 - 5), then checking if + * the 131st bit is set. If it is, then reduce: acc -= (2^130 - 5) + */ + + /* Calculate acc + -(2^130 - 5) */ + d = ( (uint64_t) acc0 + 5U ); + g0 = (uint32_t) d; + d = ( (uint64_t) acc1 + ( d >> 32 ) ); + g1 = (uint32_t) d; + d = ( (uint64_t) acc2 + ( d >> 32 ) ); + g2 = (uint32_t) d; + d = ( (uint64_t) acc3 + ( d >> 32 ) ); + g3 = (uint32_t) d; + g4 = acc4 + (uint32_t) ( d >> 32U ); + + /* mask == 0xFFFFFFFF if 131st bit is set, otherwise mask == 0 */ + mask = (uint32_t) 0U - ( g4 >> 2U ); + mask_inv = ~mask; + + /* If 131st bit is set then acc=g, otherwise, acc is unmodified */ + acc0 = ( acc0 & mask_inv ) | ( g0 & mask ); + acc1 = ( acc1 & mask_inv ) | ( g1 & mask ); + acc2 = ( acc2 & mask_inv ) | ( g2 & mask ); + acc3 = ( acc3 & mask_inv ) | ( g3 & mask ); + + /* Add 's' */ + d = (uint64_t) acc0 + ctx->s[0]; + acc0 = (uint32_t) d; + d = (uint64_t) acc1 + ctx->s[1] + ( d >> 32U ); + acc1 = (uint32_t) d; + d = (uint64_t) acc2 + ctx->s[2] + ( d >> 32U ); + acc2 = (uint32_t) d; + acc3 += ctx->s[3] + (uint32_t) ( d >> 32U ); + + /* Compute MAC (128 least significant bits of the accumulator) */ + mac[ 0] = (unsigned char)( acc0 ); + mac[ 1] = (unsigned char)( acc0 >> 8 ); + mac[ 2] = (unsigned char)( acc0 >> 16 ); + mac[ 3] = (unsigned char)( acc0 >> 24 ); + mac[ 4] = (unsigned char)( acc1 ); + mac[ 5] = (unsigned char)( acc1 >> 8 ); + mac[ 6] = (unsigned char)( acc1 >> 16 ); + mac[ 7] = (unsigned char)( acc1 >> 24 ); + mac[ 8] = (unsigned char)( acc2 ); + mac[ 9] = (unsigned char)( acc2 >> 8 ); + mac[10] = (unsigned char)( acc2 >> 16 ); + mac[11] = (unsigned char)( acc2 >> 24 ); + mac[12] = (unsigned char)( acc3 ); + mac[13] = (unsigned char)( acc3 >> 8 ); + mac[14] = (unsigned char)( acc3 >> 16 ); + mac[15] = (unsigned char)( acc3 >> 24 ); +} + +void mbedtls_poly1305_init( mbedtls_poly1305_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_poly1305_context ) ); + } +} + +void mbedtls_poly1305_free( mbedtls_poly1305_context *ctx ) +{ + if( ctx != NULL ) + { + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_poly1305_context ) ); + } +} + +int mbedtls_poly1305_starts( mbedtls_poly1305_context *ctx, + const unsigned char key[32] ) +{ + if( ctx == NULL || key == NULL ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + + /* r &= 0x0ffffffc0ffffffc0ffffffc0fffffff */ + ctx->r[0] = BYTES_TO_U32_LE( key, 0 ) & 0x0FFFFFFFU; + ctx->r[1] = BYTES_TO_U32_LE( key, 4 ) & 0x0FFFFFFCU; + ctx->r[2] = BYTES_TO_U32_LE( key, 8 ) & 0x0FFFFFFCU; + ctx->r[3] = BYTES_TO_U32_LE( key, 12 ) & 0x0FFFFFFCU; + + ctx->s[0] = BYTES_TO_U32_LE( key, 16 ); + ctx->s[1] = BYTES_TO_U32_LE( key, 20 ); + ctx->s[2] = BYTES_TO_U32_LE( key, 24 ); + ctx->s[3] = BYTES_TO_U32_LE( key, 28 ); + + /* Initial accumulator state */ + ctx->acc[0] = 0U; + ctx->acc[1] = 0U; + ctx->acc[2] = 0U; + ctx->acc[3] = 0U; + ctx->acc[4] = 0U; + + /* Queue initially empty */ + mbedtls_platform_zeroize( ctx->queue, sizeof( ctx->queue ) ); + ctx->queue_len = 0U; + + return( 0 ); +} + +int mbedtls_poly1305_update( mbedtls_poly1305_context *ctx, + const unsigned char *input, + size_t ilen ) +{ + size_t offset = 0U; + size_t remaining = ilen; + size_t queue_free_len; + size_t nblocks; + + if( ctx == NULL ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + else if( ( ilen > 0U ) && ( input == NULL ) ) + { + /* input pointer is allowed to be NULL only if ilen == 0 */ + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + + if( ( remaining > 0U ) && ( ctx->queue_len > 0U ) ) + { + queue_free_len = ( POLY1305_BLOCK_SIZE_BYTES - ctx->queue_len ); + + if( ilen < queue_free_len ) + { + /* Not enough data to complete the block. + * Store this data with the other leftovers. + */ + memcpy( &ctx->queue[ctx->queue_len], + input, + ilen ); + + ctx->queue_len += ilen; + + remaining = 0U; + } + else + { + /* Enough data to produce a complete block */ + memcpy( &ctx->queue[ctx->queue_len], + input, + queue_free_len ); + + ctx->queue_len = 0U; + + poly1305_process( ctx, 1U, ctx->queue, 1U ); /* add padding bit */ + + offset += queue_free_len; + remaining -= queue_free_len; + } + } + + if( remaining >= POLY1305_BLOCK_SIZE_BYTES ) + { + nblocks = remaining / POLY1305_BLOCK_SIZE_BYTES; + + poly1305_process( ctx, nblocks, &input[offset], 1U ); + + offset += nblocks * POLY1305_BLOCK_SIZE_BYTES; + remaining %= POLY1305_BLOCK_SIZE_BYTES; + } + + if( remaining > 0U ) + { + /* Store partial block */ + ctx->queue_len = remaining; + memcpy( ctx->queue, &input[offset], remaining ); + } + + return( 0 ); +} + +int mbedtls_poly1305_finish( mbedtls_poly1305_context *ctx, + unsigned char mac[16] ) +{ + if( ( ctx == NULL ) || ( mac == NULL ) ) + { + return( MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA ); + } + + /* Process any leftover data */ + if( ctx->queue_len > 0U ) + { + /* Add padding bit */ + ctx->queue[ctx->queue_len] = 1U; + ctx->queue_len++; + + /* Pad with zeroes */ + memset( &ctx->queue[ctx->queue_len], + 0, + POLY1305_BLOCK_SIZE_BYTES - ctx->queue_len ); + + poly1305_process( ctx, 1U, /* Process 1 block */ + ctx->queue, 0U ); /* Already padded above */ + } + + poly1305_compute_mac( ctx, mac ); + + return( 0 ); +} + +int mbedtls_poly1305_mac( const unsigned char key[32], + const unsigned char *input, + size_t ilen, + unsigned char mac[16] ) +{ + mbedtls_poly1305_context ctx; + int ret; + + mbedtls_poly1305_init( &ctx ); + + ret = mbedtls_poly1305_starts( &ctx, key ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_poly1305_update( &ctx, input, ilen ); + if( ret != 0 ) + goto cleanup; + + ret = mbedtls_poly1305_finish( &ctx, mac ); + +cleanup: + mbedtls_poly1305_free( &ctx ); + return( ret ); +} + +#endif /* MBEDTLS_POLY1305_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_keys[2][32] = +{ + { + 0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33, + 0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8, + 0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd, + 0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b + }, + { + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, + 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0, + 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, + 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0 + } +}; + +static const unsigned char test_data[2][127] = +{ + { + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, + 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x46, 0x6f, + 0x72, 0x75, 0x6d, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6f, + 0x75, 0x70 + }, + { + 0x27, 0x54, 0x77, 0x61, 0x73, 0x20, 0x62, 0x72, + 0x69, 0x6c, 0x6c, 0x69, 0x67, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x6c, 0x69, 0x74, 0x68, 0x79, 0x20, 0x74, 0x6f, + 0x76, 0x65, 0x73, 0x0a, 0x44, 0x69, 0x64, 0x20, + 0x67, 0x79, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x67, 0x69, 0x6d, 0x62, 0x6c, 0x65, 0x20, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x61, 0x62, 0x65, 0x3a, 0x0a, 0x41, 0x6c, 0x6c, + 0x20, 0x6d, 0x69, 0x6d, 0x73, 0x79, 0x20, 0x77, + 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x62, 0x6f, 0x72, 0x6f, 0x67, 0x6f, 0x76, 0x65, + 0x73, 0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x20, + 0x72, 0x61, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x75, + 0x74, 0x67, 0x72, 0x61, 0x62, 0x65, 0x2e + } +}; + +static const size_t test_data_len[2] = +{ + 34U, + 127U +}; + +static const unsigned char test_mac[2][16] = +{ + { + 0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6, + 0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9 + }, + { + 0x45, 0x41, 0x66, 0x9a, 0x7e, 0xaa, 0xee, 0x61, + 0xe7, 0x08, 0xdc, 0x7c, 0xbc, 0xc5, 0xeb, 0x62 + } +}; + +#define ASSERT( cond, args ) \ + do \ + { \ + if( ! ( cond ) ) \ + { \ + if( verbose != 0 ) \ + mbedtls_printf args; \ + \ + return( -1 ); \ + } \ + } \ + while( 0 ) + +int mbedtls_poly1305_self_test( int verbose ) +{ + unsigned char mac[16]; + unsigned i; + int ret; + + for( i = 0U; i < 2U; i++ ) + { + if( verbose != 0 ) + mbedtls_printf( " Poly1305 test %u ", i ); + + ret = mbedtls_poly1305_mac( test_keys[i], + test_data[i], + test_data_len[i], + mac ); + ASSERT( 0 == ret, ( "error code: %i\n", ret ) ); + + ASSERT( 0 == memcmp( mac, test_mac[i], 16U ), ( "failed (mac)\n" ) ); + + if( verbose != 0 ) + mbedtls_printf( "passed\n" ); + } + + if( verbose != 0 ) + mbedtls_printf( "\n" ); + + return( 0 ); +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_POLY1305_C */ diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c index 1587de4805..bab6087c4e 100644 --- a/thirdparty/mbedtls/library/sha1.c +++ b/thirdparty/mbedtls/library/sha1.c @@ -342,14 +342,6 @@ void mbedtls_sha1_update( mbedtls_sha1_context *ctx, } #endif -static const unsigned char sha1_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - /* * SHA-1 final digest */ @@ -357,25 +349,48 @@ int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx, unsigned char output[20] ) { int ret; - uint32_t last, padn; + uint32_t used; uint32_t high, low; - unsigned char msglen[8]; + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if( used <= 56 ) + { + /* Enough room for padding + length in current block */ + memset( ctx->buffer + used, 0, 56 - used ); + } + else + { + /* We'll need an extra block */ + memset( ctx->buffer + used, 0, 64 - used ); + + if( ( ret = mbedtls_internal_sha1_process( ctx, ctx->buffer ) ) != 0 ) + return( ret ); + + memset( ctx->buffer, 0, 56 ); + } + + /* + * Add message length + */ high = ( ctx->total[0] >> 29 ) | ( ctx->total[1] << 3 ); low = ( ctx->total[0] << 3 ); - PUT_UINT32_BE( high, msglen, 0 ); - PUT_UINT32_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + PUT_UINT32_BE( high, ctx->buffer, 56 ); + PUT_UINT32_BE( low, ctx->buffer, 60 ); - if( ( ret = mbedtls_sha1_update_ret( ctx, sha1_padding, padn ) ) != 0 ) - return( ret ); - if( ( ret = mbedtls_sha1_update_ret( ctx, msglen, 8 ) ) != 0 ) + if( ( ret = mbedtls_internal_sha1_process( ctx, ctx->buffer ) ) != 0 ) return( ret ); + /* + * Output final state + */ PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[2], output, 8 ); diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c index 695485d847..dbb4a89861 100644 --- a/thirdparty/mbedtls/library/sha256.c +++ b/thirdparty/mbedtls/library/sha256.c @@ -311,14 +311,6 @@ void mbedtls_sha256_update( mbedtls_sha256_context *ctx, } #endif -static const unsigned char sha256_padding[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - /* * SHA-256 final digest */ @@ -326,26 +318,48 @@ int mbedtls_sha256_finish_ret( mbedtls_sha256_context *ctx, unsigned char output[32] ) { int ret; - uint32_t last, padn; + uint32_t used; uint32_t high, low; - unsigned char msglen[8]; + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if( used <= 56 ) + { + /* Enough room for padding + length in current block */ + memset( ctx->buffer + used, 0, 56 - used ); + } + else + { + /* We'll need an extra block */ + memset( ctx->buffer + used, 0, 64 - used ); + + if( ( ret = mbedtls_internal_sha256_process( ctx, ctx->buffer ) ) != 0 ) + return( ret ); + + memset( ctx->buffer, 0, 56 ); + } + + /* + * Add message length + */ high = ( ctx->total[0] >> 29 ) | ( ctx->total[1] << 3 ); low = ( ctx->total[0] << 3 ); - PUT_UINT32_BE( high, msglen, 0 ); - PUT_UINT32_BE( low, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - if( ( ret = mbedtls_sha256_update_ret( ctx, sha256_padding, padn ) ) != 0 ) - return( ret ); + PUT_UINT32_BE( high, ctx->buffer, 56 ); + PUT_UINT32_BE( low, ctx->buffer, 60 ); - if( ( ret = mbedtls_sha256_update_ret( ctx, msglen, 8 ) ) != 0 ) + if( ( ret = mbedtls_internal_sha256_process( ctx, ctx->buffer ) ) != 0 ) return( ret ); + /* + * Output final state + */ PUT_UINT32_BE( ctx->state[0], output, 0 ); PUT_UINT32_BE( ctx->state[1], output, 4 ); PUT_UINT32_BE( ctx->state[2], output, 8 ); diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c index 6de94e99b4..a9440e8af5 100644 --- a/thirdparty/mbedtls/library/sha512.c +++ b/thirdparty/mbedtls/library/sha512.c @@ -341,18 +341,6 @@ void mbedtls_sha512_update( mbedtls_sha512_context *ctx, } #endif -static const unsigned char sha512_padding[128] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - /* * SHA-512 final digest */ @@ -360,26 +348,48 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx, unsigned char output[64] ) { int ret; - size_t last, padn; + unsigned used; uint64_t high, low; - unsigned char msglen[16]; - high = ( ctx->total[0] >> 61 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); + /* + * Add padding: 0x80 then 0x00 until 16 bytes remain for the length + */ + used = ctx->total[0] & 0x7F; - PUT_UINT64_BE( high, msglen, 0 ); - PUT_UINT64_BE( low, msglen, 8 ); + ctx->buffer[used++] = 0x80; - last = (size_t)( ctx->total[0] & 0x7F ); - padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last ); + if( used <= 112 ) + { + /* Enough room for padding + length in current block */ + memset( ctx->buffer + used, 0, 112 - used ); + } + else + { + /* We'll need an extra block */ + memset( ctx->buffer + used, 0, 128 - used ); - if( ( ret = mbedtls_sha512_update_ret( ctx, sha512_padding, padn ) ) != 0 ) + if( ( ret = mbedtls_internal_sha512_process( ctx, ctx->buffer ) ) != 0 ) return( ret ); - if( ( ret = mbedtls_sha512_update_ret( ctx, msglen, 16 ) ) != 0 ) - return( ret ); + memset( ctx->buffer, 0, 112 ); + } + + /* + * Add message length + */ + high = ( ctx->total[0] >> 61 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT64_BE( high, ctx->buffer, 112 ); + PUT_UINT64_BE( low, ctx->buffer, 120 ); + if( ( ret = mbedtls_internal_sha512_process( ctx, ctx->buffer ) ) != 0 ) + return( ret ); + + /* + * Output final state + */ PUT_UINT64_BE( ctx->state[0], output, 0 ); PUT_UINT64_BE( ctx->state[1], output, 8 ); PUT_UINT64_BE( ctx->state[2], output, 16 ); diff --git a/thirdparty/mbedtls/library/ssl_ciphersuites.c b/thirdparty/mbedtls/library/ssl_ciphersuites.c index 2e9a0fd792..59cdc7a806 100644 --- a/thirdparty/mbedtls/library/ssl_ciphersuites.c +++ b/thirdparty/mbedtls/library/ssl_ciphersuites.c @@ -47,7 +47,7 @@ * 1. By key exchange: * Forward-secure non-PSK > forward-secure PSK > ECJPAKE > other non-PSK > other PSK * 2. By key length and cipher: - * AES-256 > Camellia-256 > ARIA-256 > AES-128 > Camellia-128 > ARIA-128 > 3DES + * ChaCha > AES-256 > Camellia-256 > ARIA-256 > AES-128 > Camellia-128 > ARIA-128 > 3DES * 3. By cipher mode when relevant GCM > CCM > CBC > CCM_8 * 4. By hash function used when relevant * 5. By key exchange/auth again: EC > non-EC @@ -57,6 +57,11 @@ static const int ciphersuite_preference[] = #if defined(MBEDTLS_SSL_CIPHERSUITES) MBEDTLS_SSL_CIPHERSUITES, #else + /* Chacha-Poly ephemeral suites */ + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* All AES-256 ephemeral suites */ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, @@ -127,6 +132,8 @@ static const int ciphersuite_preference[] = MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* The PSK ephemeral suites */ + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, @@ -227,6 +234,7 @@ static const int ciphersuite_preference[] = MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* The RSA PSK suites */ + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, @@ -246,6 +254,7 @@ static const int ciphersuite_preference[] = MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, /* The PSK suites */ + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_PSK_WITH_AES_256_CCM, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, @@ -312,6 +321,75 @@ static const int ciphersuite_preference[] = static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = { +#if defined(MBEDTLS_CHACHAPOLY_C) && \ + defined(MBEDTLS_SHA256_C) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_2) +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + { MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_DHE_RSA, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + { MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + { MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + { MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + { MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_RSA_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif +#endif /* MBEDTLS_CHACHAPOLY_C && + MBEDTLS_SHA256_C && + MBEDTLS_SSL_PROTO_TLS1_2 */ #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) #if defined(MBEDTLS_AES_C) #if defined(MBEDTLS_SHA1_C) diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c index 7455e99d2e..ba59c48989 100644 --- a/thirdparty/mbedtls/library/ssl_cli.c +++ b/thirdparty/mbedtls/library/ssl_cli.c @@ -57,7 +57,7 @@ static void ssl_write_hostname_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t hostname_len; *olen = 0; @@ -127,7 +127,7 @@ static void ssl_write_renegotiation_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -171,7 +171,7 @@ static void ssl_write_signature_algorithms_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t sig_alg_len = 0; const int *md; #if defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECDSA_C) @@ -256,7 +256,7 @@ static void ssl_write_supported_elliptic_curves_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; unsigned char *elliptic_curve_list = p + 6; size_t elliptic_curve_len = 0; const mbedtls_ecp_curve_info *info; @@ -329,7 +329,7 @@ static void ssl_write_supported_point_formats_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -362,7 +362,7 @@ static void ssl_write_ecjpake_kkpp_ext( mbedtls_ssl_context *ssl, { int ret; unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t kkpp_len; *olen = 0; @@ -439,7 +439,7 @@ static void ssl_write_max_fragment_length_ext( mbedtls_ssl_context *ssl, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -472,7 +472,7 @@ static void ssl_write_truncated_hmac_ext( mbedtls_ssl_context *ssl, unsigned char *buf, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -504,7 +504,7 @@ static void ssl_write_encrypt_then_mac_ext( mbedtls_ssl_context *ssl, unsigned char *buf, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -538,7 +538,7 @@ static void ssl_write_extended_ms_ext( mbedtls_ssl_context *ssl, unsigned char *buf, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; *olen = 0; @@ -572,7 +572,7 @@ static void ssl_write_session_ticket_ext( mbedtls_ssl_context *ssl, unsigned char *buf, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t tlen = ssl->session_negotiate->ticket_len; *olen = 0; @@ -616,7 +616,7 @@ static void ssl_write_alpn_ext( mbedtls_ssl_context *ssl, unsigned char *buf, size_t *olen ) { unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t alpnlen = 0; const char **cur; @@ -1247,14 +1247,14 @@ static int ssl_parse_supported_point_formats_ext( mbedtls_ssl_context *ssl, size_t list_size; const unsigned char *p; - list_size = buf[0]; - if( list_size + 1 != len ) + if( len == 0 || (size_t)( buf[0] + 1 ) != len ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO ); } + list_size = buf[0]; p = buf + 1; while( list_size > 0 ) @@ -2117,7 +2117,7 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, size_t len_bytes = ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 ? 0 : 2; unsigned char *p = ssl->handshake->premaster + pms_offset; - if( offset + len_bytes > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( offset + len_bytes > MBEDTLS_SSL_OUT_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "buffer too small for encrypted pms" ) ); return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL ); @@ -2160,7 +2160,7 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, if( ( ret = mbedtls_pk_encrypt( &ssl->session_negotiate->peer_cert->pk, p, ssl->handshake->pmslen, ssl->out_msg + offset + len_bytes, olen, - MBEDTLS_SSL_MAX_CONTENT_LEN - offset - len_bytes, + MBEDTLS_SSL_OUT_CONTENT_LEN - offset - len_bytes, ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_rsa_pkcs1_encrypt", ret ); @@ -2544,10 +2544,9 @@ static int ssl_parse_server_key_exchange( mbedtls_ssl_context *ssl ) defined(MBEDTLS_SSL_PROTO_TLS1_2) if( md_alg != MBEDTLS_MD_NONE ) { - /* Info from md_alg will be used instead */ - hashlen = 0; - ret = mbedtls_ssl_get_key_exchange_md_tls1_2( ssl, hash, params, - params_len, md_alg ); + ret = mbedtls_ssl_get_key_exchange_md_tls1_2( ssl, hash, &hashlen, + params, params_len, + md_alg ); if( ret != 0 ) return( ret ); } @@ -2559,8 +2558,7 @@ static int ssl_parse_server_key_exchange( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - MBEDTLS_SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen != 0 ? hashlen : - (unsigned int) ( mbedtls_md_get_size( mbedtls_md_info_from_type( md_alg ) ) ) ); + MBEDTLS_SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen ); if( ssl->session_negotiate->peer_cert == NULL ) { @@ -2711,7 +2709,7 @@ static int ssl_parse_certificate_request( mbedtls_ssl_context *ssl ) * therefore the buffer length at this point must be greater than that * regardless of the actual code path. */ - if( ssl->in_hslen <= mbedtls_ssl_hs_hdr_len( ssl ) + 2 + n ) + if( ssl->in_hslen <= mbedtls_ssl_hs_hdr_len( ssl ) + 3 + n ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, @@ -2928,7 +2926,7 @@ static int ssl_write_client_key_exchange( mbedtls_ssl_context *ssl ) i = 4; n = ssl->conf->psk_identity_len; - if( i + 2 + n > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( i + 2 + n > MBEDTLS_SSL_OUT_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "psk identity too long or " "SSL buffer too short" ) ); @@ -2964,7 +2962,7 @@ static int ssl_write_client_key_exchange( mbedtls_ssl_context *ssl ) */ n = ssl->handshake->dhm_ctx.len; - if( i + 2 + n > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( i + 2 + n > MBEDTLS_SSL_OUT_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "psk identity or DHM size too long" " or SSL buffer too short" ) ); @@ -2993,7 +2991,7 @@ static int ssl_write_client_key_exchange( mbedtls_ssl_context *ssl ) * ClientECDiffieHellmanPublic public; */ ret = mbedtls_ecdh_make_public( &ssl->handshake->ecdh_ctx, &n, - &ssl->out_msg[i], MBEDTLS_SSL_MAX_CONTENT_LEN - i, + &ssl->out_msg[i], MBEDTLS_SSL_OUT_CONTENT_LEN - i, ssl->conf->f_rng, ssl->conf->p_rng ); if( ret != 0 ) { @@ -3034,7 +3032,7 @@ static int ssl_write_client_key_exchange( mbedtls_ssl_context *ssl ) i = 4; ret = mbedtls_ecjpake_write_round_two( &ssl->handshake->ecjpake_ctx, - ssl->out_msg + i, MBEDTLS_SSL_MAX_CONTENT_LEN - i, &n, + ssl->out_msg + i, MBEDTLS_SSL_OUT_CONTENT_LEN - i, &n, ssl->conf->f_rng, ssl->conf->p_rng ); if( ret != 0 ) { diff --git a/thirdparty/mbedtls/library/ssl_srv.c b/thirdparty/mbedtls/library/ssl_srv.c index 09b7a3fed3..52087ae6e1 100644 --- a/thirdparty/mbedtls/library/ssl_srv.c +++ b/thirdparty/mbedtls/library/ssl_srv.c @@ -91,6 +91,13 @@ static int ssl_parse_servername_ext( mbedtls_ssl_context *ssl, MBEDTLS_SSL_DEBUG_MSG( 3, ( "parse ServerName extension" ) ); + if( len < 2 ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } servername_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); if( servername_list_size + 2 != len ) { @@ -101,7 +108,7 @@ static int ssl_parse_servername_ext( mbedtls_ssl_context *ssl, } p = buf + 2; - while( servername_list_size > 0 ) + while( servername_list_size > 2 ) { hostname_len = ( ( p[1] << 8 ) | p[2] ); if( hostname_len + 3 > servername_list_size ) @@ -205,6 +212,12 @@ static int ssl_parse_signature_algorithms_ext( mbedtls_ssl_context *ssl, mbedtls_md_type_t md_cur; mbedtls_pk_type_t sig_cur; + if ( len < 2 ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } sig_alg_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); if( sig_alg_list_size + 2 != len || sig_alg_list_size % 2 != 0 ) @@ -273,6 +286,12 @@ static int ssl_parse_supported_elliptic_curves( mbedtls_ssl_context *ssl, const unsigned char *p; const mbedtls_ecp_curve_info *curve_info, **curves; + if ( len < 2 ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); if( list_size + 2 != len || list_size % 2 != 0 ) @@ -332,14 +351,14 @@ static int ssl_parse_supported_point_formats( mbedtls_ssl_context *ssl, size_t list_size; const unsigned char *p; - list_size = buf[0]; - if( list_size + 1 != len ) + if( len == 0 || (size_t)( buf[0] + 1 ) != len ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); } + list_size = buf[0]; p = buf + 1; while( list_size > 0 ) @@ -709,7 +728,7 @@ static int ssl_pick_cert( mbedtls_ssl_context *ssl, MBEDTLS_SSL_DEBUG_CRT( 3, "candidate certificate chain, certificate", cur->cert ); - if( ! mbedtls_pk_can_do( cur->key, pk_alg ) ) + if( ! mbedtls_pk_can_do( &cur->cert->pk, pk_alg ) ) { MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: key type" ) ); continue; @@ -733,7 +752,7 @@ static int ssl_pick_cert( mbedtls_ssl_context *ssl, #if defined(MBEDTLS_ECDSA_C) if( pk_alg == MBEDTLS_PK_ECDSA && - ssl_check_key_curve( cur->key, ssl->handshake->curves ) != 0 ) + ssl_check_key_curve( &cur->cert->pk, ssl->handshake->curves ) != 0 ) { MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: elliptic curve" ) ); continue; @@ -1303,7 +1322,7 @@ read_record_header: else #endif { - if( msg_len > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( msg_len > MBEDTLS_SSL_IN_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); @@ -1656,10 +1675,16 @@ read_record_header: while( ext_len != 0 ) { - unsigned int ext_id = ( ( ext[0] << 8 ) - | ( ext[1] ) ); - unsigned int ext_size = ( ( ext[2] << 8 ) - | ( ext[3] ) ); + unsigned int ext_id; + unsigned int ext_size; + if ( ext_len < 4 ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + ext_id = ( ( ext[0] << 8 ) | ( ext[1] ) ); + ext_size = ( ( ext[2] << 8 ) | ( ext[3] ) ); if( ext_size + 4 > ext_len ) { @@ -2235,7 +2260,7 @@ static void ssl_write_ecjpake_kkpp_ext( mbedtls_ssl_context *ssl, { int ret; unsigned char *p = buf; - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; size_t kkpp_len; *olen = 0; @@ -2342,7 +2367,7 @@ static int ssl_write_hello_verify_request( mbedtls_ssl_context *ssl ) cookie_len_byte = p++; if( ( ret = ssl->conf->f_cookie_write( ssl->conf->p_cookie, - &p, ssl->out_buf + MBEDTLS_SSL_BUFFER_LEN, + &p, ssl->out_buf + MBEDTLS_SSL_OUT_BUFFER_LEN, ssl->cli_id, ssl->cli_id_len ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "f_cookie_write", ret ); @@ -2638,7 +2663,7 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl ) size_t dn_size, total_dn_size; /* excluding length bytes */ size_t ct_len, sa_len; /* including length bytes */ unsigned char *buf, *p; - const unsigned char * const end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + const unsigned char * const end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; const mbedtls_x509_crt *crt; int authmode; @@ -2828,54 +2853,56 @@ static int ssl_get_ecdh_params_from_cert( mbedtls_ssl_context *ssl ) #endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ -static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) +#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) && \ + defined(MBEDTLS_SSL_ASYNC_PRIVATE) +static int ssl_resume_server_key_exchange( mbedtls_ssl_context *ssl, + size_t *signature_len ) +{ + /* Append the signature to ssl->out_msg, leaving 2 bytes for the + * signature length which will be added in ssl_write_server_key_exchange + * after the call to ssl_prepare_server_key_exchange. + * ssl_write_server_key_exchange also takes care of incrementing + * ssl->out_msglen. */ + unsigned char *sig_start = ssl->out_msg + ssl->out_msglen + 2; + size_t sig_max_len = ( ssl->out_buf + MBEDTLS_SSL_OUT_CONTENT_LEN + - sig_start ); + int ret = ssl->conf->f_async_resume( ssl, + sig_start, signature_len, sig_max_len ); + if( ret != MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ) + { + ssl->handshake->async_in_progress = 0; + mbedtls_ssl_set_async_operation_data( ssl, NULL ); + } + MBEDTLS_SSL_DEBUG_RET( 2, "ssl_resume_server_key_exchange", ret ); + return( ret ); +} +#endif /* defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) && + defined(MBEDTLS_SSL_ASYNC_PRIVATE) */ + +/* Prepare the ServerKeyExchange message, up to and including + * calculating the signature if any, but excluding formatting the + * signature and sending the message. */ +static int ssl_prepare_server_key_exchange( mbedtls_ssl_context *ssl, + size_t *signature_len ) { - int ret; - size_t n = 0; const mbedtls_ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; - #if defined(MBEDTLS_KEY_EXCHANGE__SOME_PFS__ENABLED) - unsigned char *p = ssl->out_msg + 4; - size_t len; #if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) - unsigned char *dig_signed = p; - size_t dig_signed_len = 0; + unsigned char *dig_signed = NULL; #endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ #endif /* MBEDTLS_KEY_EXCHANGE__SOME_PFS__ENABLED */ - MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) ); - - /* - * - * Part 1: Extract static ECDH parameters and abort - * if ServerKeyExchange not needed. - * - */ - - /* For suites involving ECDH, extract DH parameters - * from certificate at this point. */ -#if defined(MBEDTLS_KEY_EXCHANGE__SOME__ECDH_ENABLED) - if( mbedtls_ssl_ciphersuite_uses_ecdh( ciphersuite_info ) ) - { - ssl_get_ecdh_params_from_cert( ssl ); - } -#endif /* MBEDTLS_KEY_EXCHANGE__SOME__ECDH_ENABLED */ + (void) ciphersuite_info; /* unused in some configurations */ +#if !defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) + (void) signature_len; +#endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ - /* Key exchanges not involving ephemeral keys don't use - * ServerKeyExchange, so end here. */ -#if defined(MBEDTLS_KEY_EXCHANGE__SOME_NON_PFS__ENABLED) - if( mbedtls_ssl_ciphersuite_no_pfs( ciphersuite_info ) ) - { - MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write server key exchange" ) ); - ssl->state++; - return( 0 ); - } -#endif /* MBEDTLS_KEY_EXCHANGE__NON_PFS__ENABLED */ + ssl->out_msglen = 4; /* header (type:1, length:3) to be written later */ /* * - * Part 2: Provide key exchange parameters for chosen ciphersuite. + * Part 1: Provide key exchange parameters for chosen ciphersuite. * */ @@ -2885,18 +2912,21 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ) { - const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN; + int ret; + size_t len = 0; - ret = mbedtls_ecjpake_write_round_two( &ssl->handshake->ecjpake_ctx, - p, end - p, &len, ssl->conf->f_rng, ssl->conf->p_rng ); + ret = mbedtls_ecjpake_write_round_two( + &ssl->handshake->ecjpake_ctx, + ssl->out_msg + ssl->out_msglen, + MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen, &len, + ssl->conf->f_rng, ssl->conf->p_rng ); if( ret != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecjpake_write_round_two", ret ); return( ret ); } - p += len; - n += len; + ssl->out_msglen += len; } #endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ @@ -2910,10 +2940,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK || ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ) { - *(p++) = 0x00; - *(p++) = 0x00; - - n += 2; + ssl->out_msg[ssl->out_msglen++] = 0x00; + ssl->out_msg[ssl->out_msglen++] = 0x00; } #endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED || MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ @@ -2924,6 +2952,9 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_KEY_EXCHANGE__SOME__DHE_ENABLED) if( mbedtls_ssl_ciphersuite_uses_dhe( ciphersuite_info ) ) { + int ret; + size_t len = 0; + if( ssl->conf->dhm_P.p == NULL || ssl->conf->dhm_G.p == NULL ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "no DH parameters set" ) ); @@ -2947,21 +2978,21 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) return( ret ); } - if( ( ret = mbedtls_dhm_make_params( &ssl->handshake->dhm_ctx, - (int) mbedtls_mpi_size( &ssl->handshake->dhm_ctx.P ), - p, &len, ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) + if( ( ret = mbedtls_dhm_make_params( + &ssl->handshake->dhm_ctx, + (int) mbedtls_mpi_size( &ssl->handshake->dhm_ctx.P ), + ssl->out_msg + ssl->out_msglen, &len, + ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_dhm_make_params", ret ); return( ret ); } #if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) - dig_signed = p; - dig_signed_len = len; + dig_signed = ssl->out_msg + ssl->out_msglen; #endif - p += len; - n += len; + ssl->out_msglen += len; MBEDTLS_SSL_DEBUG_MPI( 3, "DHM: X ", &ssl->handshake->dhm_ctx.X ); MBEDTLS_SSL_DEBUG_MPI( 3, "DHM: P ", &ssl->handshake->dhm_ctx.P ); @@ -2986,6 +3017,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) */ const mbedtls_ecp_curve_info **curve = NULL; const mbedtls_ecp_group_id *gid; + int ret; + size_t len = 0; /* Match our preference list against the offered curves */ for( gid = ssl->conf->curve_list; *gid != MBEDTLS_ECP_DP_NONE; gid++ ) @@ -3009,21 +3042,21 @@ curve_matching_done: return( ret ); } - if( ( ret = mbedtls_ecdh_make_params( &ssl->handshake->ecdh_ctx, &len, - p, MBEDTLS_SSL_MAX_CONTENT_LEN - n, - ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) + if( ( ret = mbedtls_ecdh_make_params( + &ssl->handshake->ecdh_ctx, &len, + ssl->out_msg + ssl->out_msglen, + MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen, + ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ecdh_make_params", ret ); return( ret ); } #if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) - dig_signed = p; - dig_signed_len = len; + dig_signed = ssl->out_msg + ssl->out_msglen; #endif - p += len; - n += len; + ssl->out_msglen += len; MBEDTLS_SSL_DEBUG_ECP( 3, "ECDH: Q ", &ssl->handshake->ecdh_ctx.Q ); } @@ -3031,19 +3064,20 @@ curve_matching_done: /* * - * Part 3: For key exchanges involving the server signing the + * Part 2: For key exchanges involving the server signing the * exchange parameters, compute and add the signature here. * */ #if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) if( mbedtls_ssl_ciphersuite_uses_server_signature( ciphersuite_info ) ) { - size_t signature_len = 0; - unsigned int hashlen = 0; - unsigned char hash[64]; + size_t dig_signed_len = ssl->out_msg + ssl->out_msglen - dig_signed; + size_t hashlen = 0; + unsigned char hash[MBEDTLS_MD_MAX_SIZE]; + int ret; /* - * 3.1: Choose hash algorithm: + * 2.1: Choose hash algorithm: * A: For TLS 1.2, obey signature-hash-algorithm extension * to choose appropriate hash. * B: For SSL3, TLS1.0, TLS1.1 and ECDHE_ECDSA, use SHA1 @@ -3090,7 +3124,7 @@ curve_matching_done: MBEDTLS_SSL_DEBUG_MSG( 3, ( "pick hash algorithm %d for signing", md_alg ) ); /* - * 3.2: Compute the hash to be signed + * 2.2: Compute the hash to be signed */ #if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_1) @@ -3110,9 +3144,7 @@ curve_matching_done: defined(MBEDTLS_SSL_PROTO_TLS1_2) if( md_alg != MBEDTLS_MD_NONE ) { - /* Info from md_alg will be used instead */ - hashlen = 0; - ret = mbedtls_ssl_get_key_exchange_md_tls1_2( ssl, hash, + ret = mbedtls_ssl_get_key_exchange_md_tls1_2( ssl, hash, &hashlen, dig_signed, dig_signed_len, md_alg ); @@ -3127,18 +3159,11 @@ curve_matching_done: return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - MBEDTLS_SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen != 0 ? hashlen : - (unsigned int) ( mbedtls_md_get_size( mbedtls_md_info_from_type( md_alg ) ) ) ); + MBEDTLS_SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen ); /* - * 3.3: Compute and add the signature + * 2.3: Compute and add the signature */ - if( mbedtls_ssl_own_key( ssl ) == NULL ) - { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no private key" ) ); - return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED ); - } - #if defined(MBEDTLS_SSL_PROTO_TLS1_2) if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 ) { @@ -3158,33 +3183,150 @@ curve_matching_done: * */ - *(p++) = mbedtls_ssl_hash_from_md_alg( md_alg ); - *(p++) = mbedtls_ssl_sig_from_pk_alg( sig_alg ); - - n += 2; + ssl->out_msg[ssl->out_msglen++] = + mbedtls_ssl_hash_from_md_alg( md_alg ); + ssl->out_msg[ssl->out_msglen++] = + mbedtls_ssl_sig_from_pk_alg( sig_alg ); } #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ - if( ( ret = mbedtls_pk_sign( mbedtls_ssl_own_key( ssl ), md_alg, hash, hashlen, - p + 2 , &signature_len, ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if( ssl->conf->f_async_sign_start != NULL ) + { + ret = ssl->conf->f_async_sign_start( ssl, + mbedtls_ssl_own_cert( ssl ), + md_alg, hash, hashlen ); + switch( ret ) + { + case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH: + /* act as if f_async_sign was null */ + break; + case 0: + ssl->handshake->async_in_progress = 1; + return( ssl_resume_server_key_exchange( ssl, signature_len ) ); + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + ssl->handshake->async_in_progress = 1; + return( MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ); + default: + MBEDTLS_SSL_DEBUG_RET( 1, "f_async_sign_start", ret ); + return( ret ); + } + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if( mbedtls_ssl_own_key( ssl ) == NULL ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no private key" ) ); + return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + + /* Append the signature to ssl->out_msg, leaving 2 bytes for the + * signature length which will be added in ssl_write_server_key_exchange + * after the call to ssl_prepare_server_key_exchange. + * ssl_write_server_key_exchange also takes care of incrementing + * ssl->out_msglen. */ + if( ( ret = mbedtls_pk_sign( mbedtls_ssl_own_key( ssl ), + md_alg, hash, hashlen, + ssl->out_msg + ssl->out_msglen + 2, + signature_len, + ssl->conf->f_rng, + ssl->conf->p_rng ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_pk_sign", ret ); return( ret ); } + } +#endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ + + return( 0 ); +} + +/* Prepare the ServerKeyExchange message and send it. For ciphersuites + * that do not include a ServerKeyExchange message, do nothing. Either + * way, if successful, move on to the next step in the SSL state + * machine. */ +static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl ) +{ + int ret; + size_t signature_len = 0; +#if defined(MBEDTLS_KEY_EXCHANGE__SOME_NON_PFS__ENABLED) + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->transform_negotiate->ciphersuite_info; +#endif /* MBEDTLS_KEY_EXCHANGE__SOME_NON_PFS__ENABLED */ + + MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) ); + +#if defined(MBEDTLS_KEY_EXCHANGE__SOME_NON_PFS__ENABLED) + /* Extract static ECDH parameters and abort if ServerKeyExchange + * is not needed. */ + if( mbedtls_ssl_ciphersuite_no_pfs( ciphersuite_info ) ) + { + /* For suites involving ECDH, extract DH parameters + * from certificate at this point. */ +#if defined(MBEDTLS_KEY_EXCHANGE__SOME__ECDH_ENABLED) + if( mbedtls_ssl_ciphersuite_uses_ecdh( ciphersuite_info ) ) + { + ssl_get_ecdh_params_from_cert( ssl ); + } +#endif /* MBEDTLS_KEY_EXCHANGE__SOME__ECDH_ENABLED */ - *(p++) = (unsigned char)( signature_len >> 8 ); - *(p++) = (unsigned char)( signature_len ); - n += 2; + /* Key exchanges not involving ephemeral keys don't use + * ServerKeyExchange, so end here. */ + MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write server key exchange" ) ); + ssl->state++; + return( 0 ); + } +#endif /* MBEDTLS_KEY_EXCHANGE__SOME_NON_PFS__ENABLED */ - MBEDTLS_SSL_DEBUG_BUF( 3, "my signature", p, signature_len ); +#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) && \ + defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /* If we have already prepared the message and there is an ongoing + * signature operation, resume signing. */ + if( ssl->handshake->async_in_progress != 0 ) + { + MBEDTLS_SSL_DEBUG_MSG( 2, ( "resuming signature operation" ) ); + ret = ssl_resume_server_key_exchange( ssl, &signature_len ); + } + else +#endif /* defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) && + defined(MBEDTLS_SSL_ASYNC_PRIVATE) */ + { + /* ServerKeyExchange is needed. Prepare the message. */ + ret = ssl_prepare_server_key_exchange( ssl, &signature_len ); + } - n += signature_len; + if( ret != 0 ) + { + /* If we're starting to write a new message, set ssl->out_msglen + * to 0. But if we're resuming after an asynchronous message, + * out_msglen is the amount of data written so far and mst be + * preserved. */ + if( ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ) + MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write server key exchange (pending)" ) ); + else + ssl->out_msglen = 0; + return( ret ); } -#endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ - /* Done with actual work; add header and send. */ + /* If there is a signature, write its length. + * ssl_prepare_server_key_exchange already wrote the signature + * itself at its proper place in the output buffer. */ +#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED) + if( signature_len != 0 ) + { + ssl->out_msg[ssl->out_msglen++] = (unsigned char)( signature_len >> 8 ); + ssl->out_msg[ssl->out_msglen++] = (unsigned char)( signature_len ); - ssl->out_msglen = 4 + n; + MBEDTLS_SSL_DEBUG_BUF( 3, "my signature", + ssl->out_msg + ssl->out_msglen, + signature_len ); + + /* Skip over the already-written signature */ + ssl->out_msglen += signature_len; + } +#endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ + + /* Add header and send. */ ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; ssl->out_msg[0] = MBEDTLS_SSL_HS_SERVER_KEY_EXCHANGE; @@ -3197,7 +3339,6 @@ curve_matching_done: } MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write server key exchange" ) ); - return( 0 ); } @@ -3272,33 +3413,59 @@ static int ssl_parse_client_dh_public( mbedtls_ssl_context *ssl, unsigned char * #if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) -static int ssl_parse_encrypted_pms( mbedtls_ssl_context *ssl, - const unsigned char *p, - const unsigned char *end, - size_t pms_offset ) + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +static int ssl_resume_decrypt_pms( mbedtls_ssl_context *ssl, + unsigned char *peer_pms, + size_t *peer_pmslen, + size_t peer_pmssize ) +{ + int ret = ssl->conf->f_async_resume( ssl, + peer_pms, peer_pmslen, peer_pmssize ); + if( ret != MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ) + { + ssl->handshake->async_in_progress = 0; + mbedtls_ssl_set_async_operation_data( ssl, NULL ); + } + MBEDTLS_SSL_DEBUG_RET( 2, "ssl_decrypt_encrypted_pms", ret ); + return( ret ); +} +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +static int ssl_decrypt_encrypted_pms( mbedtls_ssl_context *ssl, + const unsigned char *p, + const unsigned char *end, + unsigned char *peer_pms, + size_t *peer_pmslen, + size_t peer_pmssize ) { int ret; - size_t len = mbedtls_pk_get_len( mbedtls_ssl_own_key( ssl ) ); - unsigned char *pms = ssl->handshake->premaster + pms_offset; - unsigned char ver[2]; - unsigned char fake_pms[48], peer_pms[48]; - unsigned char mask; - size_t i, peer_pmslen; - unsigned int diff; + mbedtls_pk_context *private_key = mbedtls_ssl_own_key( ssl ); + mbedtls_pk_context *public_key = &mbedtls_ssl_own_cert( ssl )->pk; + size_t len = mbedtls_pk_get_len( public_key ); - if( ! mbedtls_pk_can_do( mbedtls_ssl_own_key( ssl ), MBEDTLS_PK_RSA ) ) +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /* If we have already started decoding the message and there is an ongoing + * decryption operation, resume signing. */ + if( ssl->handshake->async_in_progress != 0 ) { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no RSA private key" ) ); - return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED ); + MBEDTLS_SSL_DEBUG_MSG( 2, ( "resuming decryption operation" ) ); + return( ssl_resume_decrypt_pms( ssl, + peer_pms, peer_pmslen, peer_pmssize ) ); } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ /* - * Decrypt the premaster using own private RSA key + * Prepare to decrypt the premaster using own private RSA key */ #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_2) if( ssl->minor_ver != MBEDTLS_SSL_MINOR_VERSION_0 ) { + if ( p + 2 > end ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } if( *p++ != ( ( len >> 8 ) & 0xFF ) || *p++ != ( ( len ) & 0xFF ) ) { @@ -3314,30 +3481,120 @@ static int ssl_parse_encrypted_pms( mbedtls_ssl_context *ssl, return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); } + /* + * Decrypt the premaster secret + */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if( ssl->conf->f_async_decrypt_start != NULL ) + { + ret = ssl->conf->f_async_decrypt_start( ssl, + mbedtls_ssl_own_cert( ssl ), + p, len ); + switch( ret ) + { + case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH: + /* act as if f_async_decrypt_start was null */ + break; + case 0: + ssl->handshake->async_in_progress = 1; + return( ssl_resume_decrypt_pms( ssl, + peer_pms, + peer_pmslen, + peer_pmssize ) ); + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + ssl->handshake->async_in_progress = 1; + return( MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ); + default: + MBEDTLS_SSL_DEBUG_RET( 1, "f_async_decrypt_start", ret ); + return( ret ); + } + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if( ! mbedtls_pk_can_do( private_key, MBEDTLS_PK_RSA ) ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no RSA private key" ) ); + return( MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + + ret = mbedtls_pk_decrypt( private_key, p, len, + peer_pms, peer_pmslen, peer_pmssize, + ssl->conf->f_rng, ssl->conf->p_rng ); + return( ret ); +} + +static int ssl_parse_encrypted_pms( mbedtls_ssl_context *ssl, + const unsigned char *p, + const unsigned char *end, + size_t pms_offset ) +{ + int ret; + unsigned char *pms = ssl->handshake->premaster + pms_offset; + unsigned char ver[2]; + unsigned char fake_pms[48], peer_pms[48]; + unsigned char mask; + size_t i, peer_pmslen; + unsigned int diff; + + /* In case of a failure in decryption, the decryption may write less than + * 2 bytes of output, but we always read the first two bytes. It doesn't + * matter in the end because diff will be nonzero in that case due to + * peer_pmslen being less than 48, and we only care whether diff is 0. + * But do initialize peer_pms for robustness anyway. This also makes + * memory analyzers happy (don't access uninitialized memory, even + * if it's an unsigned char). */ + peer_pms[0] = peer_pms[1] = ~0; + + ret = ssl_decrypt_encrypted_pms( ssl, p, end, + peer_pms, + &peer_pmslen, + sizeof( peer_pms ) ); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if ( ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS ) + return( ret ); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + mbedtls_ssl_write_version( ssl->handshake->max_major_ver, - ssl->handshake->max_minor_ver, - ssl->conf->transport, ver ); + ssl->handshake->max_minor_ver, + ssl->conf->transport, ver ); + + /* Avoid data-dependent branches while checking for invalid + * padding, to protect against timing-based Bleichenbacher-type + * attacks. */ + diff = (unsigned int) ret; + diff |= peer_pmslen ^ 48; + diff |= peer_pms[0] ^ ver[0]; + diff |= peer_pms[1] ^ ver[1]; + + /* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */ + /* MSVC has a warning about unary minus on unsigned, but this is + * well-defined and precisely what we want to do here */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + mask = - ( ( diff | - diff ) >> ( sizeof( unsigned int ) * 8 - 1 ) ); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif /* * Protection against Bleichenbacher's attack: invalid PKCS#1 v1.5 padding * must not cause the connection to end immediately; instead, send a * bad_record_mac later in the handshake. - * Also, avoid data-dependant branches here to protect against - * timing-based variants. + * To protect against timing-based variants of the attack, we must + * not have any branch that depends on whether the decryption was + * successful. In particular, always generate the fake premaster secret, + * regardless of whether it will ultimately influence the output or not. */ ret = ssl->conf->f_rng( ssl->conf->p_rng, fake_pms, sizeof( fake_pms ) ); if( ret != 0 ) + { + /* It's ok to abort on an RNG failure, since this does not reveal + * anything about the RSA decryption. */ return( ret ); - - ret = mbedtls_pk_decrypt( mbedtls_ssl_own_key( ssl ), p, len, - peer_pms, &peer_pmslen, - sizeof( peer_pms ), - ssl->conf->f_rng, ssl->conf->p_rng ); - - diff = (unsigned int) ret; - diff |= peer_pmslen ^ 48; - diff |= peer_pms[0] ^ ver[0]; - diff |= peer_pms[1] ^ ver[1]; + } #if defined(MBEDTLS_SSL_DEBUG_ALL) if( diff != 0 ) @@ -3352,18 +3609,8 @@ static int ssl_parse_encrypted_pms( mbedtls_ssl_context *ssl, } ssl->handshake->pmslen = 48; - /* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */ - /* MSVC has a warning about unary minus on unsigned, but this is - * well-defined and precisely what we want to do here */ -#if defined(_MSC_VER) -#pragma warning( push ) -#pragma warning( disable : 4146 ) -#endif - mask = - ( ( diff | - diff ) >> ( sizeof( unsigned int ) * 8 - 1 ) ); -#if defined(_MSC_VER) -#pragma warning( pop ) -#endif - + /* Set pms to either the true or the fake PMS, without + * data-dependent branches. */ for( i = 0; i < ssl->handshake->pmslen; i++ ) pms[i] = ( mask & fake_pms[i] ) | ( (~mask) & peer_pms[i] ); @@ -3445,6 +3692,20 @@ static int ssl_parse_client_key_exchange( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse client key exchange" ) ); +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) && \ + ( defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) ) + if( ( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA ) && + ( ssl->handshake->async_in_progress != 0 ) ) + { + /* We've already read a record and there is an asynchronous + * operation in progress to decrypt it. So skip reading the + * record. */ + MBEDTLS_SSL_DEBUG_MSG( 3, ( "will resume decryption of previously-read record" ) ); + } + else +#endif if( ( ret = mbedtls_ssl_read_record( ssl ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_read_record", ret ); @@ -3557,6 +3818,19 @@ static int ssl_parse_client_key_exchange( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ) { +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if ( ssl->handshake->async_in_progress != 0 ) + { + /* There is an asynchronous operation in progress to + * decrypt the encrypted premaster secret, so skip + * directly to resuming this operation. */ + MBEDTLS_SSL_DEBUG_MSG( 3, ( "PSK identity already parsed" ) ); + /* Update p to skip the PSK identity. ssl_parse_encrypted_pms + * won't actually use it, but maintain p anyway for robustness. */ + p += ssl->conf->psk_identity_len + 2; + } + else +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ if( ( ret = ssl_parse_client_psk_identity( ssl, &p, end ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, ( "ssl_parse_client_psk_identity" ), ret ); @@ -3926,7 +4200,7 @@ static int ssl_write_new_session_ticket( mbedtls_ssl_context *ssl ) if( ( ret = ssl->conf->f_ticket_write( ssl->conf->p_ticket, ssl->session_negotiate, ssl->out_msg + 10, - ssl->out_msg + MBEDTLS_SSL_MAX_CONTENT_LEN, + ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN, &tlen, &lifetime ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_ticket_write", ret ); diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c index e8e0cd854b..91f96c8ab6 100644 --- a/thirdparty/mbedtls/library/ssl_tls.c +++ b/thirdparty/mbedtls/library/ssl_tls.c @@ -141,14 +141,24 @@ static void ssl_reset_retransmit_timeout( mbedtls_ssl_context *ssl ) * } MaxFragmentLength; * and we add 0 -> extension unused */ -static unsigned int mfl_code_to_length[MBEDTLS_SSL_MAX_FRAG_LEN_INVALID] = +static unsigned int ssl_mfl_code_to_length( int mfl ) { - MBEDTLS_SSL_MAX_CONTENT_LEN, /* MBEDTLS_SSL_MAX_FRAG_LEN_NONE */ - 512, /* MBEDTLS_SSL_MAX_FRAG_LEN_512 */ - 1024, /* MBEDTLS_SSL_MAX_FRAG_LEN_1024 */ - 2048, /* MBEDTLS_SSL_MAX_FRAG_LEN_2048 */ - 4096, /* MBEDTLS_SSL_MAX_FRAG_LEN_4096 */ -}; + switch( mfl ) + { + case MBEDTLS_SSL_MAX_FRAG_LEN_NONE: + return ( MBEDTLS_TLS_EXT_ADV_CONTENT_LEN ); + case MBEDTLS_SSL_MAX_FRAG_LEN_512: + return 512; + case MBEDTLS_SSL_MAX_FRAG_LEN_1024: + return 1024; + case MBEDTLS_SSL_MAX_FRAG_LEN_2048: + return 2048; + case MBEDTLS_SSL_MAX_FRAG_LEN_4096: + return 4096; + default: + return ( MBEDTLS_TLS_EXT_ADV_CONTENT_LEN ); + } +} #endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ #if defined(MBEDTLS_SSL_CLI_C) @@ -688,18 +698,32 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) transform->keylen = cipher_info->key_bitlen / 8; if( cipher_info->mode == MBEDTLS_MODE_GCM || - cipher_info->mode == MBEDTLS_MODE_CCM ) + cipher_info->mode == MBEDTLS_MODE_CCM || + cipher_info->mode == MBEDTLS_MODE_CHACHAPOLY ) { + size_t taglen, explicit_ivlen; + transform->maclen = 0; mac_key_len = 0; + /* All modes haves 96-bit IVs; + * GCM and CCM has 4 implicit and 8 explicit bytes + * ChachaPoly has all 12 bytes implicit + */ transform->ivlen = 12; - transform->fixed_ivlen = 4; + if( cipher_info->mode == MBEDTLS_MODE_CHACHAPOLY ) + transform->fixed_ivlen = 12; + else + transform->fixed_ivlen = 4; + + /* All modes have 128-bit tags, except CCM_8 (ciphersuite flag) */ + taglen = transform->ciphersuite_info->flags & + MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16; - /* Minimum length is expicit IV + tag */ - transform->minlen = transform->ivlen - transform->fixed_ivlen - + ( transform->ciphersuite_info->flags & - MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16 ); + + /* Minimum length of encrypted record */ + explicit_ivlen = transform->ivlen - transform->fixed_ivlen; + transform->minlen = explicit_ivlen + taglen; } else { @@ -956,11 +980,11 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl ) if( ssl->compress_buf == NULL ) { MBEDTLS_SSL_DEBUG_MSG( 3, ( "Allocating compression buffer" ) ); - ssl->compress_buf = mbedtls_calloc( 1, MBEDTLS_SSL_BUFFER_LEN ); + ssl->compress_buf = mbedtls_calloc( 1, MBEDTLS_SSL_COMPRESS_BUFFER_LEN ); if( ssl->compress_buf == NULL ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed", - MBEDTLS_SSL_BUFFER_LEN ) ); + MBEDTLS_SSL_COMPRESS_BUFFER_LEN ) ); return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); } } @@ -1151,6 +1175,9 @@ int mbedtls_ssl_psk_derive_premaster( mbedtls_ssl_context *ssl, mbedtls_key_exch * other_secret already set by the ClientKeyExchange message, * and is 48 bytes long */ + if( end - p < 2 ) + return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); + *p++ = 0; *p++ = 48; p += 48; @@ -1276,6 +1303,27 @@ static void ssl_mac( mbedtls_md_context_t *md_ctx, #define SSL_SOME_MODES_USE_MAC #endif +/* The function below is only used in the Lucky 13 counter-measure in + * ssl_decrypt_buf(). These are the defines that guard the call site. */ +#if defined(SSL_SOME_MODES_USE_MAC) && \ + ( defined(MBEDTLS_SSL_PROTO_TLS1) || \ + defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ + defined(MBEDTLS_SSL_PROTO_TLS1_2) ) +/* This function makes sure every byte in the memory region is accessed + * (in ascending addresses order) */ +static void ssl_read_memory( unsigned char *p, size_t len ) +{ + unsigned char acc = 0; + volatile unsigned char force; + + for( ; len != 0; p++, len-- ) + acc ^= *p; + + force = acc; + (void) force; +} +#endif /* SSL_SOME_MODES_USE_MAC && ( TLS1 || TLS1_1 || TLS1_2 ) */ + /* * Encryption/decryption functions */ @@ -1297,11 +1345,11 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_BUF( 4, "before encrypt: output payload", ssl->out_msg, ssl->out_msglen ); - if( ssl->out_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( ssl->out_msglen > MBEDTLS_SSL_OUT_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "Record content %u too large, maximum %d", (unsigned) ssl->out_msglen, - MBEDTLS_SSL_MAX_CONTENT_LEN ) ); + MBEDTLS_SSL_OUT_CONTENT_LEN ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -1394,17 +1442,26 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl ) } else #endif /* MBEDTLS_ARC4_C || MBEDTLS_CIPHER_NULL_CIPHER */ -#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) if( mode == MBEDTLS_MODE_GCM || - mode == MBEDTLS_MODE_CCM ) + mode == MBEDTLS_MODE_CCM || + mode == MBEDTLS_MODE_CHACHAPOLY ) { int ret; size_t enc_msglen, olen; unsigned char *enc_msg; unsigned char add_data[13]; - unsigned char taglen = ssl->transform_out->ciphersuite_info->flags & + unsigned char iv[12]; + mbedtls_ssl_transform *transform = ssl->transform_out; + unsigned char taglen = transform->ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16; + size_t explicit_ivlen = transform->ivlen - transform->fixed_ivlen; + /* + * Prepare additional authenticated data + */ memcpy( add_data, ssl->out_ctr, 8 ); add_data[8] = ssl->out_msgtype; mbedtls_ssl_write_version( ssl->major_ver, ssl->minor_ver, @@ -1412,44 +1469,57 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl ) add_data[11] = ( ssl->out_msglen >> 8 ) & 0xFF; add_data[12] = ssl->out_msglen & 0xFF; - MBEDTLS_SSL_DEBUG_BUF( 4, "additional data used for AEAD", - add_data, 13 ); + MBEDTLS_SSL_DEBUG_BUF( 4, "additional data for AEAD", add_data, 13 ); /* * Generate IV */ - if( ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen != 8 ) + if( transform->ivlen == 12 && transform->fixed_ivlen == 4 ) + { + /* GCM and CCM: fixed || explicit (=seqnum) */ + memcpy( iv, transform->iv_enc, transform->fixed_ivlen ); + memcpy( iv + transform->fixed_ivlen, ssl->out_ctr, 8 ); + memcpy( ssl->out_iv, ssl->out_ctr, 8 ); + + } + else if( transform->ivlen == 12 && transform->fixed_ivlen == 12 ) + { + /* ChachaPoly: fixed XOR sequence number */ + unsigned char i; + + memcpy( iv, transform->iv_enc, transform->fixed_ivlen ); + + for( i = 0; i < 8; i++ ) + iv[i+4] ^= ssl->out_ctr[i]; + } + else { /* Reminder if we ever add an AEAD mode with a different size */ MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - memcpy( ssl->transform_out->iv_enc + ssl->transform_out->fixed_ivlen, - ssl->out_ctr, 8 ); - memcpy( ssl->out_iv, ssl->out_ctr, 8 ); - - MBEDTLS_SSL_DEBUG_BUF( 4, "IV used", ssl->out_iv, - ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); + MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (internal)", + iv, transform->ivlen ); + MBEDTLS_SSL_DEBUG_BUF( 4, "IV used (transmitted)", + ssl->out_iv, explicit_ivlen ); /* - * Fix pointer positions and message length with added IV + * Fix message length with added IV */ enc_msg = ssl->out_msg; enc_msglen = ssl->out_msglen; - ssl->out_msglen += ssl->transform_out->ivlen - - ssl->transform_out->fixed_ivlen; + ssl->out_msglen += explicit_ivlen; MBEDTLS_SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " - "including %d bytes of padding", - ssl->out_msglen, 0 ) ); + "including 0 bytes of padding", + ssl->out_msglen ) ); /* * Encrypt and authenticate */ - if( ( ret = mbedtls_cipher_auth_encrypt( &ssl->transform_out->cipher_ctx_enc, - ssl->transform_out->iv_enc, - ssl->transform_out->ivlen, + if( ( ret = mbedtls_cipher_auth_encrypt( &transform->cipher_ctx_enc, + iv, transform->ivlen, add_data, 13, enc_msg, enc_msglen, enc_msg, &olen, @@ -1609,7 +1679,6 @@ static int ssl_encrypt_buf( mbedtls_ssl_context *ssl ) static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) { - size_t i; mbedtls_cipher_mode_t mode; int auth_done = 0; #if defined(SSL_SOME_MODES_USE_MAC) @@ -1659,20 +1728,27 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) } else #endif /* MBEDTLS_ARC4_C || MBEDTLS_CIPHER_NULL_CIPHER */ -#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) if( mode == MBEDTLS_MODE_GCM || - mode == MBEDTLS_MODE_CCM ) + mode == MBEDTLS_MODE_CCM || + mode == MBEDTLS_MODE_CHACHAPOLY ) { int ret; size_t dec_msglen, olen; unsigned char *dec_msg; unsigned char *dec_msg_result; unsigned char add_data[13]; - unsigned char taglen = ssl->transform_in->ciphersuite_info->flags & + unsigned char iv[12]; + mbedtls_ssl_transform *transform = ssl->transform_in; + unsigned char taglen = transform->ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16; - size_t explicit_iv_len = ssl->transform_in->ivlen - - ssl->transform_in->fixed_ivlen; + size_t explicit_iv_len = transform->ivlen - transform->fixed_ivlen; + /* + * Compute and update sizes + */ if( ssl->in_msglen < explicit_iv_len + taglen ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "msglen (%d) < explicit_iv_len (%d) " @@ -1686,6 +1762,9 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) dec_msg_result = ssl->in_msg; ssl->in_msglen = dec_msglen; + /* + * Prepare additional authenticated data + */ memcpy( add_data, ssl->in_ctr, 8 ); add_data[8] = ssl->in_msgtype; mbedtls_ssl_write_version( ssl->major_ver, ssl->minor_ver, @@ -1693,23 +1772,43 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) add_data[11] = ( ssl->in_msglen >> 8 ) & 0xFF; add_data[12] = ssl->in_msglen & 0xFF; - MBEDTLS_SSL_DEBUG_BUF( 4, "additional data used for AEAD", - add_data, 13 ); + MBEDTLS_SSL_DEBUG_BUF( 4, "additional data for AEAD", add_data, 13 ); - memcpy( ssl->transform_in->iv_dec + ssl->transform_in->fixed_ivlen, - ssl->in_iv, - ssl->transform_in->ivlen - ssl->transform_in->fixed_ivlen ); + /* + * Prepare IV + */ + if( transform->ivlen == 12 && transform->fixed_ivlen == 4 ) + { + /* GCM and CCM: fixed || explicit (transmitted) */ + memcpy( iv, transform->iv_dec, transform->fixed_ivlen ); + memcpy( iv + transform->fixed_ivlen, ssl->in_iv, 8 ); - MBEDTLS_SSL_DEBUG_BUF( 4, "IV used", ssl->transform_in->iv_dec, - ssl->transform_in->ivlen ); + } + else if( transform->ivlen == 12 && transform->fixed_ivlen == 12 ) + { + /* ChachaPoly: fixed XOR sequence number */ + unsigned char i; + + memcpy( iv, transform->iv_dec, transform->fixed_ivlen ); + + for( i = 0; i < 8; i++ ) + iv[i+4] ^= ssl->in_ctr[i]; + } + else + { + /* Reminder if we ever add an AEAD mode with a different size */ + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); + } + + MBEDTLS_SSL_DEBUG_BUF( 4, "IV used", iv, transform->ivlen ); MBEDTLS_SSL_DEBUG_BUF( 4, "TAG used", dec_msg + dec_msglen, taglen ); /* * Decrypt and authenticate */ if( ( ret = mbedtls_cipher_auth_decrypt( &ssl->transform_in->cipher_ctx_dec, - ssl->transform_in->iv_dec, - ssl->transform_in->ivlen, + iv, transform->ivlen, add_data, 13, dec_msg, dec_msglen, dec_msg_result, &olen, @@ -1827,6 +1926,7 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) */ if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_2 ) { + unsigned char i; dec_msglen -= ssl->transform_in->ivlen; ssl->in_msglen -= ssl->transform_in->ivlen; @@ -1900,27 +2000,28 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) * and fake check up to 256 bytes of padding */ size_t pad_count = 0, real_count = 1; - size_t padding_idx = ssl->in_msglen - padlen - 1; + size_t padding_idx = ssl->in_msglen - padlen; + size_t i; /* * Padding is guaranteed to be incorrect if: - * 1. padlen >= ssl->in_msglen + * 1. padlen > ssl->in_msglen * - * 2. padding_idx >= MBEDTLS_SSL_MAX_CONTENT_LEN + + * 2. padding_idx > MBEDTLS_SSL_IN_CONTENT_LEN + * ssl->transform_in->maclen * * In both cases we reset padding_idx to a safe value (0) to * prevent out-of-buffer reads. */ - correct &= ( ssl->in_msglen >= padlen + 1 ); - correct &= ( padding_idx < MBEDTLS_SSL_MAX_CONTENT_LEN + + correct &= ( padlen <= ssl->in_msglen ); + correct &= ( padding_idx <= MBEDTLS_SSL_IN_CONTENT_LEN + ssl->transform_in->maclen ); padding_idx *= correct; - for( i = 1; i <= 256; i++ ) + for( i = 0; i < 256; i++ ) { - real_count &= ( i <= padlen ); + real_count &= ( i < padlen ); pad_count += real_count * ( ssl->in_msg[padding_idx + i] == padlen - 1 ); } @@ -1951,8 +2052,10 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } +#if defined(MBEDTLS_SSL_DEBUG_ALL) MBEDTLS_SSL_DEBUG_BUF( 4, "raw buffer after decryption", ssl->in_msg, ssl->in_msglen ); +#endif /* * Authenticate if not done yet. @@ -1985,20 +2088,69 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) { /* * Process MAC and always update for padlen afterwards to make - * total time independent of padlen - * - * extra_run compensates MAC check for padlen + * total time independent of padlen. * * Known timing attacks: * - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) * - * We use ( ( Lx + 8 ) / 64 ) to handle 'negative Lx' values - * correctly. (We round down instead of up, so -56 is the correct - * value for our calculations instead of -55) + * To compensate for different timings for the MAC calculation + * depending on how much padding was removed (which is determined + * by padlen), process extra_run more blocks through the hash + * function. + * + * The formula in the paper is + * extra_run = ceil( (L1-55) / 64 ) - ceil( (L2-55) / 64 ) + * where L1 is the size of the header plus the decrypted message + * plus CBC padding and L2 is the size of the header plus the + * decrypted message. This is for an underlying hash function + * with 64-byte blocks. + * We use ( (Lx+8) / 64 ) to handle 'negative Lx' values + * correctly. We round down instead of up, so -56 is the correct + * value for our calculations instead of -55. + * + * Repeat the formula rather than defining a block_size variable. + * This avoids requiring division by a variable at runtime + * (which would be marginally less efficient and would require + * linking an extra division function in some builds). */ size_t j, extra_run = 0; - extra_run = ( 13 + ssl->in_msglen + padlen + 8 ) / 64 - - ( 13 + ssl->in_msglen + 8 ) / 64; + + /* + * The next two sizes are the minimum and maximum values of + * in_msglen over all padlen values. + * + * They're independent of padlen, since we previously did + * in_msglen -= padlen. + * + * Note that max_len + maclen is never more than the buffer + * length, as we previously did in_msglen -= maclen too. + */ + const size_t max_len = ssl->in_msglen + padlen; + const size_t min_len = ( max_len > 256 ) ? max_len - 256 : 0; + + switch( ssl->transform_in->ciphersuite_info->mac ) + { +#if defined(MBEDTLS_MD5_C) || defined(MBEDTLS_SHA1_C) || \ + defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_MD5: + case MBEDTLS_MD_SHA1: + case MBEDTLS_MD_SHA256: + /* 8 bytes of message size, 64-byte compression blocks */ + extra_run = ( 13 + ssl->in_msglen + padlen + 8 ) / 64 - + ( 13 + ssl->in_msglen + 8 ) / 64; + break; +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA384: + /* 16 bytes of message size, 128-byte compression blocks */ + extra_run = ( 13 + ssl->in_msglen + padlen + 16 ) / 128 - + ( 13 + ssl->in_msglen + 16 ) / 128; + break; +#endif + default: + MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); + } extra_run &= correct * 0xFF; @@ -2007,12 +2159,25 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) mbedtls_md_hmac_update( &ssl->transform_in->md_ctx_dec, ssl->in_len, 2 ); mbedtls_md_hmac_update( &ssl->transform_in->md_ctx_dec, ssl->in_msg, ssl->in_msglen ); + /* Make sure we access everything even when padlen > 0. This + * makes the synchronisation requirements for just-in-time + * Prime+Probe attacks much tighter and hopefully impractical. */ + ssl_read_memory( ssl->in_msg + ssl->in_msglen, padlen ); mbedtls_md_hmac_finish( &ssl->transform_in->md_ctx_dec, mac_expect ); - /* Call mbedtls_md_process at least once due to cache attacks */ + + /* Call mbedtls_md_process at least once due to cache attacks + * that observe whether md_process() was called of not */ for( j = 0; j < extra_run + 1; j++ ) mbedtls_md_process( &ssl->transform_in->md_ctx_dec, ssl->in_msg ); mbedtls_md_hmac_reset( &ssl->transform_in->md_ctx_dec ); + + /* Make sure we access all the memory that could contain the MAC, + * before we check it in the next code block. This makes the + * synchronisation requirements for just-in-time Prime+Probe + * attacks much tighter and hopefully impractical. */ + ssl_read_memory( ssl->in_msg + min_len, + max_len - min_len + ssl->transform_in->maclen ); } else #endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \ @@ -2022,9 +2187,11 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } +#if defined(MBEDTLS_SSL_DEBUG_ALL) MBEDTLS_SSL_DEBUG_BUF( 4, "expected mac", mac_expect, ssl->transform_in->maclen ); MBEDTLS_SSL_DEBUG_BUF( 4, "message mac", ssl->in_msg + ssl->in_msglen, ssl->transform_in->maclen ); +#endif if( mbedtls_ssl_safer_memcmp( ssl->in_msg + ssl->in_msglen, mac_expect, ssl->transform_in->maclen ) != 0 ) @@ -2053,6 +2220,16 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) if( ssl->in_msglen == 0 ) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_3 + && ssl->in_msgtype != MBEDTLS_SSL_MSG_APPLICATION_DATA ) + { + /* TLS v1.2 explicitly disallows zero-length messages which are not application data */ + MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid zero-length message type: %d", ssl->in_msgtype ) ); + return( MBEDTLS_ERR_SSL_INVALID_RECORD ); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + ssl->nb_zero++; /* @@ -2077,6 +2254,7 @@ static int ssl_decrypt_buf( mbedtls_ssl_context *ssl ) else #endif { + unsigned char i; for( i = 8; i > ssl_ep_len( ssl ); i-- ) if( ++ssl->in_ctr[i - 1] != 0 ) break; @@ -2126,7 +2304,7 @@ static int ssl_compress_buf( mbedtls_ssl_context *ssl ) ssl->transform_out->ctx_deflate.next_in = msg_pre; ssl->transform_out->ctx_deflate.avail_in = len_pre; ssl->transform_out->ctx_deflate.next_out = msg_post; - ssl->transform_out->ctx_deflate.avail_out = MBEDTLS_SSL_BUFFER_LEN - bytes_written; + ssl->transform_out->ctx_deflate.avail_out = MBEDTLS_SSL_OUT_BUFFER_LEN - bytes_written; ret = deflate( &ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH ); if( ret != Z_OK ) @@ -2135,7 +2313,7 @@ static int ssl_compress_buf( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_COMPRESSION_FAILED ); } - ssl->out_msglen = MBEDTLS_SSL_BUFFER_LEN - + ssl->out_msglen = MBEDTLS_SSL_OUT_BUFFER_LEN - ssl->transform_out->ctx_deflate.avail_out - bytes_written; MBEDTLS_SSL_DEBUG_MSG( 3, ( "after compression: msglen = %d, ", @@ -2173,7 +2351,7 @@ static int ssl_decompress_buf( mbedtls_ssl_context *ssl ) ssl->transform_in->ctx_inflate.next_in = msg_pre; ssl->transform_in->ctx_inflate.avail_in = len_pre; ssl->transform_in->ctx_inflate.next_out = msg_post; - ssl->transform_in->ctx_inflate.avail_out = MBEDTLS_SSL_BUFFER_LEN - + ssl->transform_in->ctx_inflate.avail_out = MBEDTLS_SSL_IN_BUFFER_LEN - header_bytes; ret = inflate( &ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH ); @@ -2183,7 +2361,7 @@ static int ssl_decompress_buf( mbedtls_ssl_context *ssl ) return( MBEDTLS_ERR_SSL_COMPRESSION_FAILED ); } - ssl->in_msglen = MBEDTLS_SSL_BUFFER_LEN - + ssl->in_msglen = MBEDTLS_SSL_IN_BUFFER_LEN - ssl->transform_in->ctx_inflate.avail_out - header_bytes; MBEDTLS_SSL_DEBUG_MSG( 3, ( "after decompression: msglen = %d, ", @@ -2258,7 +2436,7 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want ) return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } - if( nb_want > MBEDTLS_SSL_BUFFER_LEN - (size_t)( ssl->in_hdr - ssl->in_buf ) ) + if( nb_want > MBEDTLS_SSL_IN_BUFFER_LEN - (size_t)( ssl->in_hdr - ssl->in_buf ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "requesting more data than fits" ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); @@ -2344,7 +2522,7 @@ int mbedtls_ssl_fetch_input( mbedtls_ssl_context *ssl, size_t nb_want ) } else { - len = MBEDTLS_SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf ); + len = MBEDTLS_SSL_IN_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf ); if( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER ) timeout = ssl->handshake->retransmit_timeout; @@ -2798,12 +2976,12 @@ int mbedtls_ssl_write_record( mbedtls_ssl_context *ssl ) if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM ) { /* Make room for the additional DTLS fields */ - if( MBEDTLS_SSL_MAX_CONTENT_LEN - ssl->out_msglen < 8 ) + if( MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen < 8 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "DTLS handshake message too large: " "size %u, maximum %u", (unsigned) ( ssl->in_hslen - 4 ), - (unsigned) ( MBEDTLS_SSL_MAX_CONTENT_LEN - 12 ) ) ); + (unsigned) ( MBEDTLS_SSL_OUT_CONTENT_LEN - 12 ) ) ); return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -3016,7 +3194,7 @@ static int ssl_reassemble_dtls_handshake( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_MSG( 2, ( "initialize reassembly, total length = %d", msg_len ) ); - if( ssl->in_hslen > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( ssl->in_hslen > MBEDTLS_SSL_IN_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "handshake message too large" ) ); return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE ); @@ -3120,7 +3298,7 @@ static int ssl_reassemble_dtls_handshake( mbedtls_ssl_context *ssl ) ssl->next_record_offset = new_remain - ssl->in_hdr; ssl->in_left = ssl->next_record_offset + remain_len; - if( ssl->in_left > MBEDTLS_SSL_BUFFER_LEN - + if( ssl->in_left > MBEDTLS_SSL_IN_BUFFER_LEN - (size_t)( ssl->in_hdr - ssl->in_buf ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "reassembled message too large for buffer" ) ); @@ -3496,7 +3674,7 @@ static int ssl_handle_possible_reconnect( mbedtls_ssl_context *ssl ) ssl->conf->p_cookie, ssl->cli_id, ssl->cli_id_len, ssl->in_buf, ssl->in_left, - ssl->out_buf, MBEDTLS_SSL_MAX_CONTENT_LEN, &len ); + ssl->out_buf, MBEDTLS_SSL_OUT_CONTENT_LEN, &len ); MBEDTLS_SSL_DEBUG_RET( 2, "ssl_check_dtls_clihlo_cookie", ret ); @@ -3593,7 +3771,7 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl ) } /* Check length against the size of our buffer */ - if( ssl->in_msglen > MBEDTLS_SSL_BUFFER_LEN + if( ssl->in_msglen > MBEDTLS_SSL_IN_BUFFER_LEN - (size_t)( ssl->in_msg - ssl->in_buf ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) ); @@ -3687,7 +3865,7 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl ) if( ssl->transform_in == NULL ) { if( ssl->in_msglen < 1 || - ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN ) + ssl->in_msglen > MBEDTLS_SSL_IN_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) ); return( MBEDTLS_ERR_SSL_INVALID_RECORD ); @@ -3703,7 +3881,7 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl ) #if defined(MBEDTLS_SSL_PROTO_SSL3) if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 && - ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN ) + ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_IN_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) ); return( MBEDTLS_ERR_SSL_INVALID_RECORD ); @@ -3716,7 +3894,7 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl ) */ if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 && ssl->in_msglen > ssl->transform_in->minlen + - MBEDTLS_SSL_MAX_CONTENT_LEN + 256 ) + MBEDTLS_SSL_IN_CONTENT_LEN + 256 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) ); return( MBEDTLS_ERR_SSL_INVALID_RECORD ); @@ -3764,7 +3942,7 @@ static int ssl_prepare_record_content( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_BUF( 4, "input payload after decrypt", ssl->in_msg, ssl->in_msglen ); - if( ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN ) + if( ssl->in_msglen > MBEDTLS_SSL_IN_CONTENT_LEN ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) ); return( MBEDTLS_ERR_SSL_INVALID_RECORD ); @@ -4096,6 +4274,16 @@ int mbedtls_ssl_handle_message_type( mbedtls_ssl_context *ssl ) if( ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT ) { + if( ssl->in_msglen != 2 ) + { + /* Note: Standard allows for more than one 2 byte alert + to be packed in a single message, but Mbed TLS doesn't + currently support this. */ + MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid alert message, len: %d", + ssl->in_msglen ) ); + return( MBEDTLS_ERR_SSL_INVALID_RECORD ); + } + MBEDTLS_SSL_DEBUG_MSG( 2, ( "got an alert message, type: [%d:%d]", ssl->in_msg[0], ssl->in_msg[1] ) ); @@ -4325,10 +4513,10 @@ int mbedtls_ssl_write_certificate( mbedtls_ssl_context *ssl ) while( crt != NULL ) { n = crt->raw.len; - if( n > MBEDTLS_SSL_MAX_CONTENT_LEN - 3 - i ) + if( n > MBEDTLS_SSL_OUT_CONTENT_LEN - 3 - i ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "certificate too large, %d > %d", - i + 3 + n, MBEDTLS_SSL_MAX_CONTENT_LEN ) ); + i + 3 + n, MBEDTLS_SSL_OUT_CONTENT_LEN ) ); return( MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE ); } @@ -4528,6 +4716,12 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) while( i < ssl->in_hslen ) { + if ( i + 3 > ssl->in_hslen ) { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR ); + return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE ); + } if( ssl->in_msg[i] != 0 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); @@ -5202,7 +5396,7 @@ static void ssl_handshake_wrapup_free_hs_transform( mbedtls_ssl_context *ssl ) /* * Free our handshake params */ - mbedtls_ssl_handshake_free( ssl->handshake ); + mbedtls_ssl_handshake_free( ssl ); mbedtls_free( ssl->handshake ); ssl->handshake = NULL; @@ -5557,7 +5751,7 @@ static int ssl_handshake_init( mbedtls_ssl_context *ssl ) if( ssl->session_negotiate ) mbedtls_ssl_session_free( ssl->session_negotiate ); if( ssl->handshake ) - mbedtls_ssl_handshake_free( ssl->handshake ); + mbedtls_ssl_handshake_free( ssl ); /* * Either the pointers are now NULL or cleared properly and can be freed. @@ -5662,17 +5856,23 @@ int mbedtls_ssl_setup( mbedtls_ssl_context *ssl, const mbedtls_ssl_config *conf ) { int ret; - const size_t len = MBEDTLS_SSL_BUFFER_LEN; ssl->conf = conf; /* * Prepare base structures */ - if( ( ssl-> in_buf = mbedtls_calloc( 1, len ) ) == NULL || - ( ssl->out_buf = mbedtls_calloc( 1, len ) ) == NULL ) + ssl->in_buf = mbedtls_calloc( 1, MBEDTLS_SSL_IN_BUFFER_LEN ); + if( ssl->in_buf == NULL ) { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed", len ) ); + MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed", MBEDTLS_SSL_IN_BUFFER_LEN) ); + return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); + } + + ssl->out_buf = mbedtls_calloc( 1, MBEDTLS_SSL_OUT_BUFFER_LEN ); + if( ssl->out_buf == NULL ) + { + MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed", MBEDTLS_SSL_OUT_BUFFER_LEN) ); mbedtls_free( ssl->in_buf ); ssl->in_buf = NULL; return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); @@ -5773,9 +5973,9 @@ static int ssl_session_reset_int( mbedtls_ssl_context *ssl, int partial ) ssl->transform_in = NULL; ssl->transform_out = NULL; - memset( ssl->out_buf, 0, MBEDTLS_SSL_BUFFER_LEN ); + memset( ssl->out_buf, 0, MBEDTLS_SSL_OUT_BUFFER_LEN ); if( partial == 0 ) - memset( ssl->in_buf, 0, MBEDTLS_SSL_BUFFER_LEN ); + memset( ssl->in_buf, 0, MBEDTLS_SSL_IN_BUFFER_LEN ); #if defined(MBEDTLS_SSL_HW_RECORD_ACCEL) if( mbedtls_ssl_hw_record_reset != NULL ) @@ -5995,27 +6195,27 @@ static int ssl_append_key_cert( mbedtls_ssl_key_cert **head, mbedtls_x509_crt *cert, mbedtls_pk_context *key ) { - mbedtls_ssl_key_cert *new; + mbedtls_ssl_key_cert *new_cert; - new = mbedtls_calloc( 1, sizeof( mbedtls_ssl_key_cert ) ); - if( new == NULL ) + new_cert = mbedtls_calloc( 1, sizeof( mbedtls_ssl_key_cert ) ); + if( new_cert == NULL ) return( MBEDTLS_ERR_SSL_ALLOC_FAILED ); - new->cert = cert; - new->key = key; - new->next = NULL; + new_cert->cert = cert; + new_cert->key = key; + new_cert->next = NULL; /* Update head is the list was null, else add to the end */ if( *head == NULL ) { - *head = new; + *head = new_cert; } else { mbedtls_ssl_key_cert *cur = *head; while( cur->next != NULL ) cur = cur->next; - cur->next = new; + cur->next = new_cert; } return( 0 ); @@ -6100,7 +6300,7 @@ int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf, /* Identity len will be encoded on two bytes */ if( ( psk_identity_len >> 16 ) != 0 || - psk_identity_len > MBEDTLS_SSL_MAX_CONTENT_LEN ) + psk_identity_len > MBEDTLS_SSL_OUT_CONTENT_LEN ) { return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -6401,7 +6601,7 @@ void mbedtls_ssl_conf_arc4_support( mbedtls_ssl_config *conf, char arc4 ) int mbedtls_ssl_conf_max_frag_len( mbedtls_ssl_config *conf, unsigned char mfl_code ) { if( mfl_code >= MBEDTLS_SSL_MAX_FRAG_LEN_INVALID || - mfl_code_to_length[mfl_code] > MBEDTLS_SSL_MAX_CONTENT_LEN ) + ssl_mfl_code_to_length( mfl_code ) > MBEDTLS_TLS_EXT_ADV_CONTENT_LEN ) { return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA ); } @@ -6480,6 +6680,43 @@ void mbedtls_ssl_conf_export_keys_cb( mbedtls_ssl_config *conf, } #endif +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +void mbedtls_ssl_conf_async_private_cb( + mbedtls_ssl_config *conf, + mbedtls_ssl_async_sign_t *f_async_sign, + mbedtls_ssl_async_decrypt_t *f_async_decrypt, + mbedtls_ssl_async_resume_t *f_async_resume, + mbedtls_ssl_async_cancel_t *f_async_cancel, + void *async_config_data ) +{ + conf->f_async_sign_start = f_async_sign; + conf->f_async_decrypt_start = f_async_decrypt; + conf->f_async_resume = f_async_resume; + conf->f_async_cancel = f_async_cancel; + conf->p_async_config_data = async_config_data; +} + +void *mbedtls_ssl_conf_get_async_config_data( const mbedtls_ssl_config *conf ) +{ + return( conf->p_async_config_data ); +} + +void *mbedtls_ssl_get_async_operation_data( const mbedtls_ssl_context *ssl ) +{ + if( ssl->handshake == NULL ) + return( NULL ); + else + return( ssl->handshake->user_async_ctx ); +} + +void mbedtls_ssl_set_async_operation_data( mbedtls_ssl_context *ssl, + void *ctx ) +{ + if( ssl->handshake != NULL ) + ssl->handshake->user_async_ctx = ctx; +} +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + /* * SSL get accessors */ @@ -6642,15 +6879,15 @@ size_t mbedtls_ssl_get_max_frag_len( const mbedtls_ssl_context *ssl ) /* * Assume mfl_code is correct since it was checked when set */ - max_len = mfl_code_to_length[ssl->conf->mfl_code]; + max_len = ssl_mfl_code_to_length( ssl->conf->mfl_code ); /* * Check if a smaller max length was negotiated */ if( ssl->session_out != NULL && - mfl_code_to_length[ssl->session_out->mfl_code] < max_len ) + ssl_mfl_code_to_length( ssl->session_out->mfl_code ) < max_len ) { - max_len = mfl_code_to_length[ssl->session_out->mfl_code]; + max_len = ssl_mfl_code_to_length( ssl->session_out->mfl_code ); } return max_len; @@ -7194,8 +7431,16 @@ int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len ) } /* - * Send application data to be encrypted by the SSL layer, - * taking care of max fragment length and buffer size + * Send application data to be encrypted by the SSL layer, taking care of max + * fragment length and buffer size. + * + * According to RFC 5246 Section 6.2.1: + * + * Zero-length fragments of Application data MAY be sent as they are + * potentially useful as a traffic analysis countermeasure. + * + * Therefore, it is possible that the input message length is 0 and the + * corresponding return code is 0 on success. */ static int ssl_write_real( mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len ) @@ -7204,7 +7449,7 @@ static int ssl_write_real( mbedtls_ssl_context *ssl, #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) size_t max_len = mbedtls_ssl_get_max_frag_len( ssl ); #else - size_t max_len = MBEDTLS_SSL_MAX_CONTENT_LEN; + size_t max_len = MBEDTLS_SSL_OUT_CONTENT_LEN; #endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ if( len > max_len ) { @@ -7223,6 +7468,12 @@ static int ssl_write_real( mbedtls_ssl_context *ssl, if( ssl->out_left != 0 ) { + /* + * The user has previously tried to send the data and + * MBEDTLS_ERR_SSL_WANT_WRITE or the message was only partially + * written. In this case, we expect the high-level write function + * (e.g. mbedtls_ssl_write()) to be called with the same parameters + */ if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flush_output", ret ); @@ -7231,6 +7482,11 @@ static int ssl_write_real( mbedtls_ssl_context *ssl, } else { + /* + * The user is trying to send a message the first time, so we need to + * copy the data into the internal buffers and setup the data structure + * to keep track of partial writes + */ ssl->out_msglen = len; ssl->out_msgtype = MBEDTLS_SSL_MSG_APPLICATION_DATA; memcpy( ssl->out_msg, buf, len ); @@ -7387,11 +7643,21 @@ static void ssl_key_cert_free( mbedtls_ssl_key_cert *key_cert ) } #endif /* MBEDTLS_X509_CRT_PARSE_C */ -void mbedtls_ssl_handshake_free( mbedtls_ssl_handshake_params *handshake ) +void mbedtls_ssl_handshake_free( mbedtls_ssl_context *ssl ) { + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + if( handshake == NULL ) return; +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if( ssl->conf->f_async_cancel != NULL && handshake->async_in_progress != 0 ) + { + ssl->conf->f_async_cancel( ssl ); + handshake->async_in_progress = 0; + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + #if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_1) mbedtls_md5_free( &handshake->fin_md5 ); @@ -7496,20 +7762,20 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl ) if( ssl->out_buf != NULL ) { - mbedtls_platform_zeroize( ssl->out_buf, MBEDTLS_SSL_BUFFER_LEN ); + mbedtls_platform_zeroize( ssl->out_buf, MBEDTLS_SSL_OUT_BUFFER_LEN ); mbedtls_free( ssl->out_buf ); } if( ssl->in_buf != NULL ) { - mbedtls_platform_zeroize( ssl->in_buf, MBEDTLS_SSL_BUFFER_LEN ); + mbedtls_platform_zeroize( ssl->in_buf, MBEDTLS_SSL_IN_BUFFER_LEN ); mbedtls_free( ssl->in_buf ); } #if defined(MBEDTLS_ZLIB_SUPPORT) if( ssl->compress_buf != NULL ) { - mbedtls_platform_zeroize( ssl->compress_buf, MBEDTLS_SSL_BUFFER_LEN ); + mbedtls_platform_zeroize( ssl->compress_buf, MBEDTLS_SSL_COMPRESS_BUFFER_LEN ); mbedtls_free( ssl->compress_buf ); } #endif @@ -7522,7 +7788,7 @@ void mbedtls_ssl_free( mbedtls_ssl_context *ssl ) if( ssl->handshake ) { - mbedtls_ssl_handshake_free( ssl->handshake ); + mbedtls_ssl_handshake_free( ssl ); mbedtls_ssl_transform_free( ssl->transform_negotiate ); mbedtls_ssl_session_free( ssl->session_negotiate ); @@ -8289,13 +8555,14 @@ exit: #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_2) int mbedtls_ssl_get_key_exchange_md_tls1_2( mbedtls_ssl_context *ssl, - unsigned char *output, - unsigned char *data, size_t data_len, - mbedtls_md_type_t md_alg ) + unsigned char *hash, size_t *hashlen, + unsigned char *data, size_t data_len, + mbedtls_md_type_t md_alg ) { int ret = 0; mbedtls_md_context_t ctx; const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type( md_alg ); + *hashlen = mbedtls_md_get_size( md_info ); mbedtls_md_init( &ctx ); @@ -8326,7 +8593,7 @@ int mbedtls_ssl_get_key_exchange_md_tls1_2( mbedtls_ssl_context *ssl, MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_md_update", ret ); goto exit; } - if( ( ret = mbedtls_md_finish( &ctx, output ) ) != 0 ) + if( ( ret = mbedtls_md_finish( &ctx, hash ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_md_finish", ret ); goto exit; diff --git a/thirdparty/mbedtls/library/threading.c b/thirdparty/mbedtls/library/threading.c index f1c37245c7..7a32e672c7 100644 --- a/thirdparty/mbedtls/library/threading.c +++ b/thirdparty/mbedtls/library/threading.c @@ -114,9 +114,6 @@ void mbedtls_threading_set_alt( void (*mutex_init)( mbedtls_threading_mutex_t * #if defined(MBEDTLS_FS_IO) mbedtls_mutex_init( &mbedtls_threading_readdir_mutex ); #endif -#if defined(MBEDTLS_HAVE_TIME_DATE) - mbedtls_mutex_init( &mbedtls_threading_gmtime_mutex ); -#endif } /* @@ -127,9 +124,6 @@ void mbedtls_threading_free_alt( void ) #if defined(MBEDTLS_FS_IO) mbedtls_mutex_free( &mbedtls_threading_readdir_mutex ); #endif -#if defined(MBEDTLS_HAVE_TIME_DATE) - mbedtls_mutex_free( &mbedtls_threading_gmtime_mutex ); -#endif } #endif /* MBEDTLS_THREADING_ALT */ @@ -142,8 +136,5 @@ void mbedtls_threading_free_alt( void ) #if defined(MBEDTLS_FS_IO) mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex MUTEX_INIT; #endif -#if defined(MBEDTLS_HAVE_TIME_DATE) -mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex MUTEX_INIT; -#endif #endif /* MBEDTLS_THREADING_C */ diff --git a/thirdparty/mbedtls/library/timing.c b/thirdparty/mbedtls/library/timing.c index 6a30e51259..3e8139f1f9 100644 --- a/thirdparty/mbedtls/library/timing.c +++ b/thirdparty/mbedtls/library/timing.c @@ -39,7 +39,8 @@ #if !defined(MBEDTLS_TIMING_ALT) #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ - !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h" #endif diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c index e8e448f6f8..777b6034c4 100644 --- a/thirdparty/mbedtls/library/version_features.c +++ b/thirdparty/mbedtls/library/version_features.c @@ -39,6 +39,9 @@ static const char *features[] = { #if defined(MBEDTLS_NO_UDBL_DIVISION) "MBEDTLS_NO_UDBL_DIVISION", #endif /* MBEDTLS_NO_UDBL_DIVISION */ +#if defined(MBEDTLS_NO_64BIT_MULTIPLICATION) + "MBEDTLS_NO_64BIT_MULTIPLICATION", +#endif /* MBEDTLS_NO_64BIT_MULTIPLICATION */ #if defined(MBEDTLS_HAVE_SSE2) "MBEDTLS_HAVE_SSE2", #endif /* MBEDTLS_HAVE_SSE2 */ @@ -102,6 +105,12 @@ static const char *features[] = { #if defined(MBEDTLS_CCM_ALT) "MBEDTLS_CCM_ALT", #endif /* MBEDTLS_CCM_ALT */ +#if defined(MBEDTLS_CHACHA20_ALT) + "MBEDTLS_CHACHA20_ALT", +#endif /* MBEDTLS_CHACHA20_ALT */ +#if defined(MBEDTLS_CHACHAPOLY_ALT) + "MBEDTLS_CHACHAPOLY_ALT", +#endif /* MBEDTLS_CHACHAPOLY_ALT */ #if defined(MBEDTLS_CMAC_ALT) "MBEDTLS_CMAC_ALT", #endif /* MBEDTLS_CMAC_ALT */ @@ -117,6 +126,9 @@ static const char *features[] = { #if defined(MBEDTLS_GCM_ALT) "MBEDTLS_GCM_ALT", #endif /* MBEDTLS_GCM_ALT */ +#if defined(MBEDTLS_NIST_KW_ALT) + "MBEDTLS_NIST_KW_ALT", +#endif /* MBEDTLS_NIST_KW_ALT */ #if defined(MBEDTLS_MD2_ALT) "MBEDTLS_MD2_ALT", #endif /* MBEDTLS_MD2_ALT */ @@ -126,6 +138,9 @@ static const char *features[] = { #if defined(MBEDTLS_MD5_ALT) "MBEDTLS_MD5_ALT", #endif /* MBEDTLS_MD5_ALT */ +#if defined(MBEDTLS_POLY1305_ALT) + "MBEDTLS_POLY1305_ALT", +#endif /* MBEDTLS_POLY1305_ALT */ #if defined(MBEDTLS_RIPEMD160_ALT) "MBEDTLS_RIPEMD160_ALT", #endif /* MBEDTLS_RIPEMD160_ALT */ @@ -255,6 +270,12 @@ static const char *features[] = { #if defined(MBEDTLS_CIPHER_MODE_CTR) "MBEDTLS_CIPHER_MODE_CTR", #endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) + "MBEDTLS_CIPHER_MODE_OFB", +#endif /* MBEDTLS_CIPHER_MODE_OFB */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) + "MBEDTLS_CIPHER_MODE_XTS", +#endif /* MBEDTLS_CIPHER_MODE_XTS */ #if defined(MBEDTLS_CIPHER_NULL_CIPHER) "MBEDTLS_CIPHER_NULL_CIPHER", #endif /* MBEDTLS_CIPHER_NULL_CIPHER */ @@ -405,6 +426,9 @@ static const char *features[] = { #if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES) "MBEDTLS_SSL_ALL_ALERT_MESSAGES", #endif /* MBEDTLS_SSL_ALL_ALERT_MESSAGES */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + "MBEDTLS_SSL_ASYNC_PRIVATE", +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ #if defined(MBEDTLS_SSL_DEBUG_ALL) "MBEDTLS_SSL_DEBUG_ALL", #endif /* MBEDTLS_SSL_DEBUG_ALL */ @@ -543,6 +567,12 @@ static const char *features[] = { #if defined(MBEDTLS_CERTS_C) "MBEDTLS_CERTS_C", #endif /* MBEDTLS_CERTS_C */ +#if defined(MBEDTLS_CHACHA20_C) + "MBEDTLS_CHACHA20_C", +#endif /* MBEDTLS_CHACHA20_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + "MBEDTLS_CHACHAPOLY_C", +#endif /* MBEDTLS_CHACHAPOLY_C */ #if defined(MBEDTLS_CIPHER_C) "MBEDTLS_CIPHER_C", #endif /* MBEDTLS_CIPHER_C */ @@ -585,9 +615,15 @@ static const char *features[] = { #if defined(MBEDTLS_HAVEGE_C) "MBEDTLS_HAVEGE_C", #endif /* MBEDTLS_HAVEGE_C */ +#if defined(MBEDTLS_HKDF_C) + "MBEDTLS_HKDF_C", +#endif /* MBEDTLS_HKDF_C */ #if defined(MBEDTLS_HMAC_DRBG_C) "MBEDTLS_HMAC_DRBG_C", #endif /* MBEDTLS_HMAC_DRBG_C */ +#if defined(MBEDTLS_NIST_KW_C) + "MBEDTLS_NIST_KW_C", +#endif /* MBEDTLS_NIST_KW_C */ #if defined(MBEDTLS_MD_C) "MBEDTLS_MD_C", #endif /* MBEDTLS_MD_C */ @@ -639,6 +675,9 @@ static const char *features[] = { #if defined(MBEDTLS_PLATFORM_C) "MBEDTLS_PLATFORM_C", #endif /* MBEDTLS_PLATFORM_C */ +#if defined(MBEDTLS_POLY1305_C) + "MBEDTLS_POLY1305_C", +#endif /* MBEDTLS_POLY1305_C */ #if defined(MBEDTLS_RIPEMD160_C) "MBEDTLS_RIPEMD160_C", #endif /* MBEDTLS_RIPEMD160_C */ diff --git a/thirdparty/mbedtls/library/x509.c b/thirdparty/mbedtls/library/x509.c index 371d6da1dc..2e6795f750 100644 --- a/thirdparty/mbedtls/library/x509.c +++ b/thirdparty/mbedtls/library/x509.c @@ -29,6 +29,10 @@ * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ +/* Ensure gmtime_r is available even with -std=c99; must be included before + * config.h, which pulls in glibc's features.h. Harmless on other platforms. */ +#define _POSIX_C_SOURCE 200112L + #if !defined(MBEDTLS_CONFIG_FILE) #include "mbedtls/config.h" #else @@ -59,26 +63,13 @@ #define mbedtls_snprintf snprintf #endif - #if defined(MBEDTLS_HAVE_TIME) #include "mbedtls/platform_time.h" #endif - -#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) -#include <windows.h> -#else +#if defined(MBEDTLS_HAVE_TIME_DATE) #include <time.h> #endif -#if defined(MBEDTLS_FS_IO) -#include <stdio.h> -#if !defined(_WIN32) -#include <sys/types.h> -#include <sys/stat.h> -#include <dirent.h> -#endif -#endif - #define CHECK(code) if( ( ret = code ) != 0 ){ return( ret ); } #define CHECK_RANGE(min, max, val) if( val < min || val > max ){ return( ret ); } @@ -903,36 +894,18 @@ int mbedtls_x509_key_size_helper( char *buf, size_t buf_size, const char *name ) * Set the time structure to the current time. * Return 0 on success, non-zero on failure. */ -#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) -static int x509_get_current_time( mbedtls_x509_time *now ) -{ - SYSTEMTIME st; - - GetSystemTime( &st ); - - now->year = st.wYear; - now->mon = st.wMonth; - now->day = st.wDay; - now->hour = st.wHour; - now->min = st.wMinute; - now->sec = st.wSecond; - - return( 0 ); -} -#else static int x509_get_current_time( mbedtls_x509_time *now ) { - struct tm *lt; + struct tm *lt, tm_buf; mbedtls_time_t tt; int ret = 0; -#if defined(MBEDTLS_THREADING_C) - if( mbedtls_mutex_lock( &mbedtls_threading_gmtime_mutex ) != 0 ) - return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); -#endif - tt = mbedtls_time( NULL ); - lt = gmtime( &tt ); +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + lt = gmtime_s( &tm_buf, &tt ) == 0 ? &tm_buf : NULL; +#else + lt = gmtime_r( &tt, &tm_buf ); +#endif if( lt == NULL ) ret = -1; @@ -946,14 +919,8 @@ static int x509_get_current_time( mbedtls_x509_time *now ) now->sec = lt->tm_sec; } -#if defined(MBEDTLS_THREADING_C) - if( mbedtls_mutex_unlock( &mbedtls_threading_gmtime_mutex ) != 0 ) - return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); -#endif - return( ret ); } -#endif /* _WIN32 && !EFIX64 && !EFI32 */ /* * Return 0 if before <= after, 1 otherwise diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c index 038eae0257..3cf1743821 100644 --- a/thirdparty/mbedtls/library/x509_crt.c +++ b/thirdparty/mbedtls/library/x509_crt.c @@ -1139,7 +1139,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) char filename[MAX_PATH]; char *p; size_t len = strlen( path ); - int lengthAsInt = 0; + int length_as_int = 0; WIN32_FIND_DATAW file_data; HANDLE hFind; @@ -1154,7 +1154,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) p = filename + len; filename[len++] = '*'; - if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) ) + if ( FAILED ( SizeTToInt( len, &length_as_int ) ) ) return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); /* @@ -1165,7 +1165,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) * incoming string are less than MAX_PATH to avoid a buffer overrun with * MultiByteToWideChar(). */ - w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir, + w_ret = MultiByteToWideChar( CP_ACP, 0, filename, length_as_int, szDir, MAX_PATH - 3 ); if( w_ret == 0 ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); @@ -1182,11 +1182,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path ) if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) continue; - if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) ) + if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &length_as_int ) ) ) return( MBEDTLS_ERR_X509_FILE_IO_ERROR ); w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName, - lengthAsInt, + length_as_int, p, (int) len - 1, NULL, NULL ); if( w_ret == 0 ) diff --git a/thirdparty/mbedtls/library/x509_csr.c b/thirdparty/mbedtls/library/x509_csr.c index 3e8e8fbc6a..f84425728a 100644 --- a/thirdparty/mbedtls/library/x509_csr.c +++ b/thirdparty/mbedtls/library/x509_csr.c @@ -274,34 +274,25 @@ int mbedtls_x509_csr_parse( mbedtls_x509_csr *csr, const unsigned char *buf, siz return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); #if defined(MBEDTLS_PEM_PARSE_C) - mbedtls_pem_init( &pem ); - /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ - if( buf[buflen - 1] != '\0' ) - ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; - else + if( buf[buflen - 1] == '\0' ) + { + mbedtls_pem_init( &pem ); ret = mbedtls_pem_read_buffer( &pem, "-----BEGIN CERTIFICATE REQUEST-----", "-----END CERTIFICATE REQUEST-----", buf, NULL, 0, &use_len ); - if( ret == 0 ) - { - /* - * Was PEM encoded, parse the result - */ - if( ( ret = mbedtls_x509_csr_parse_der( csr, pem.buf, pem.buflen ) ) != 0 ) - return( ret ); + if( ret == 0 ) + /* + * Was PEM encoded, parse the result + */ + ret = mbedtls_x509_csr_parse_der( csr, pem.buf, pem.buflen ); mbedtls_pem_free( &pem ); - return( 0 ); - } - else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - { - mbedtls_pem_free( &pem ); - return( ret ); + if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + return( ret ); } - else #endif /* MBEDTLS_PEM_PARSE_C */ return( mbedtls_x509_csr_parse_der( csr, buf, buflen ) ); } diff --git a/thirdparty/thekla_atlas/nvmath/nvmath.h b/thirdparty/thekla_atlas/nvmath/nvmath.h index f2b69426e1..a697f9293d 100644 --- a/thirdparty/thekla_atlas/nvmath/nvmath.h +++ b/thirdparty/thekla_atlas/nvmath/nvmath.h @@ -181,10 +181,8 @@ namespace nv { #if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO return _finite(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS +#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX return isfinite(f); -#elif NV_OS_LINUX - return finitef(f); #else # error "isFinite not supported" #endif @@ -196,10 +194,8 @@ namespace nv { #if NV_OS_WIN32 || NV_OS_XBOX || NV_OS_DURANGO return _isnan(f) != 0; -#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS +#elif NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD || NV_OS_ORBIS || NV_OS_LINUX return isnan(f); -#elif NV_OS_LINUX - return isnanf(f); #else # error "isNan not supported" #endif |